/*====================================================================*
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
 -  This software is distributed in the hope that it will be
 -  useful, but with NO WARRANTY OF ANY KIND.
 -  No author or distributor accepts responsibility to anyone for the
 -  consequences of using this software, or for whether it serves any
 -  particular purpose or works at all, unless he or she says so in
 -  writing.  Everyone is granted permission to copy, modify and
 -  redistribute this source code, for commercial or non-commercial
 -  purposes, with the following restrictions: (1) the origin of this
 -  source code must not be misrepresented; (2) modified versions must
 -  be plainly marked as such; and (3) this notice may not be removed
 -  or altered from any source or modified source distribution.
 *====================================================================*/

/*
 *  bardecode.c
 *
 *      Dispatcher
 *          char            *barcodeDispatchDecoder()
 *
 *      Format Determination
 *          static l_int32   barcodeFindFormat()
 *          l_int32          barcodeFormatIsSupported()
 *          static l_int32   barcodeVerifyFormat()
 *
 *      Decode 2 of 5
 *          static char     *barcodeDecode2of5()
 *
 *      Decode Interleaved 2 of 5
 *          static char     *barcodeDecodeI2of5()
 *
 *      Decode Code 93
 *          static char     *barcodeDecode93()
 *
 *      Decode Code 39
 *          static char     *barcodeDecode39()
 *
 *      Decode Codabar
 *          static char     *barcodeDecodeCodabar()
 *
 *      Decode UPC-A
 *          static char     *barcodeDecodeUpca()
 *
 *      Decode EAN 13
 *          static char     *barcodeDecodeEan13()
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
#include "readbarcode.h"


static l_int32 barcodeFindFormat(char *barstr);
static l_int32 barcodeVerifyFormat(char *barstr, l_int32 format,
                                   l_int32 *pvalid, l_int32 *preverse);
static char *barcodeDecode2of5(char *barstr, l_int32 debugflag);
static char *barcodeDecodeI2of5(char *barstr, l_int32 debugflag);
static char *barcodeDecode93(char *barstr, l_int32 debugflag);
static char *barcodeDecode39(char *barstr, l_int32 debugflag);
static char *barcodeDecodeCodabar(char *barstr, l_int32 debugflag);
static char *barcodeDecodeUpca(char *barstr, l_int32 debugflag);
static char *barcodeDecodeEan13(char *barstr, l_int32 first, l_int32 debugflag);


#ifndef  NO_CONSOLE_IO
#define  DEBUG_CODES       0
#endif  /* ~NO_CONSOLE_IO */


/*------------------------------------------------------------------------*
 *                           Decoding dispatcher                          *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDispatchDecoder()
 *
 *      Input:  barstr (string of integers in set {1,2,3,4} of bar widths)
 *              format (L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ...)
 *              debugflag (use 1 to generate debug output)
 *      Return: data (string of decoded barcode data), or null on error
 */
char *
barcodeDispatchDecoder(char    *barstr,
                       l_int32  format,
                       l_int32  debugflag)
{
char  *data = NULL;

    PROCNAME("barcodeDispatchDecoder");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

    debugflag = FALSE;  /* not used yet */

    if (format == L_BF_ANY)
        format = barcodeFindFormat(barstr);

    if (format == L_BF_CODE2OF5)
        data = barcodeDecode2of5(barstr, debugflag);
    else if (format == L_BF_CODEI2OF5)
        data = barcodeDecodeI2of5(barstr, debugflag);
    else if (format == L_BF_CODE93)
        data = barcodeDecode93(barstr, debugflag);
    else if (format == L_BF_CODE39)
    	data = barcodeDecode39(barstr, debugflag);
    else if (format == L_BF_CODABAR)
    	data = barcodeDecodeCodabar(barstr, debugflag);
    else if (format == L_BF_UPCA)
    	data = barcodeDecodeUpca(barstr, debugflag);
    else if (format == L_BF_EAN13)
    	data = barcodeDecodeEan13(barstr, 0, debugflag);
    else
        return (char *)ERROR_PTR("format not implemented", procName, NULL);

    return data;
}


/*------------------------------------------------------------------------*
 *                      Barcode format determination                      *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeFindFormat()
 *
 *      Input:  barstr (of barcode widths, in set {1,2,3,4})
 *      Return: format (for barcode), or L_BF_UNKNOWN if not recognized
 */
static l_int32
barcodeFindFormat(char    *barstr)
{
l_int32  i, format, valid;

   PROCNAME("barcodeFindFormat");

   if (!barstr)
       return ERROR_INT("barstr not defined", procName, L_BF_UNKNOWN);

   for (i = 0; i < NumSupportedBarcodeFormats; i++) {
       format = SupportedBarcodeFormat[i];
       barcodeVerifyFormat(barstr, format, &valid, NULL);
       if (valid) {
           L_INFO_STRING("Barcode format: %s", procName,
                         SupportedBarcodeFormatName[i]);
           return format;
       }
   }
   return L_BF_UNKNOWN;
}


/*!
 *  barcodeFormatIsSupported()
 *
 *      Input:  format
 *      Return: 1 if format is one of those supported; 0 otherwise
 *
 */
l_int32
barcodeFormatIsSupported(l_int32  format)
{
l_int32  i;

   for (i = 0; i < NumSupportedBarcodeFormats; i++) {
       if (format == SupportedBarcodeFormat[i])
           return 1;
   }
   return 0;
}


/*!
 *  barcodeVerifyFormat()
 *
 *      Input:  barstr (of barcode widths, in set {1,2,3,4})
 *              format (L_BF_CODEI2OF5, L_BF_CODE93, ...)
 *              &valid (<return> 0 if not valid, 1 and 2 if valid)
 *              &reverse (<optional return> 1 if reversed; 0 otherwise)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) If valid == 1, the barcode is of the given format in the
 *          forward order; if valid == 2, it is backwards.
 *      (2) If the barcode needs to be reversed to read it, and &reverse
 *          is provided, a 1 is put into @reverse.
 *      (3) Add to this as more formats are supported.
 */
static l_int32
barcodeVerifyFormat(char     *barstr,
                    l_int32   format,
                    l_int32  *pvalid,
                    l_int32  *preverse)
{
char    *revbarstr;
l_int32  i, start, len, stop, mid;

    PROCNAME("barcodeVerifyFormat");

    if (!pvalid)
        return ERROR_INT("barstr not defined", procName, 1);
    *pvalid = 0;
    if (preverse) *preverse = 0;
    if (!barstr)
        return ERROR_INT("barstr not defined", procName, 1);

    switch (format)
    {
    case L_BF_CODE2OF5:
        start = !strncmp(barstr, Code2of5[C25_START], 3);
        len = strlen(barstr);
        stop = !strncmp(&barstr[len - 5], Code2of5[C25_STOP], 5);
        if (start && stop)
            *pvalid = 1;
        else {
            revbarstr = stringReverse(barstr);
            start = !strncmp(revbarstr, Code2of5[C25_START], 3);
            stop = !strncmp(&revbarstr[len - 5], Code2of5[C25_STOP], 5);
            FREE(revbarstr);
            if (start && stop) {
                *pvalid = 1;
                if (preverse) *preverse = 1;
            }
        }
        break;
    case L_BF_CODEI2OF5:
        start = !strncmp(barstr, CodeI2of5[CI25_START], 4);
        len = strlen(barstr);
        stop = !strncmp(&barstr[len - 3], CodeI2of5[CI25_STOP], 3);
        if (start && stop)
            *pvalid = 1;
        else {
            revbarstr = stringReverse(barstr);
            start = !strncmp(revbarstr, CodeI2of5[CI25_START], 4);
            stop = !strncmp(&revbarstr[len - 3], CodeI2of5[CI25_STOP], 3);
            FREE(revbarstr);
            if (start && stop) {
                *pvalid = 1;
                if (preverse) *preverse = 1;
            }
        }
        break;
    case L_BF_CODE93:
        start = !strncmp(barstr, Code93[C93_START], 6);
        len = strlen(barstr);
        stop = !strncmp(&barstr[len - 7], Code93[C93_STOP], 6);
        if (start && stop)
            *pvalid = 1;
        else {
            revbarstr = stringReverse(barstr);
            start = !strncmp(revbarstr, Code93[C93_START], 6);
            stop = !strncmp(&revbarstr[len - 7], Code93[C93_STOP], 6);
            FREE(revbarstr);
            if (start && stop) {
                *pvalid = 1;
                if (preverse) *preverse = 1;
            }
        }
        break;
    case L_BF_CODE39:
        start = !strncmp(barstr, Code39[C39_START], 9);
        len = strlen(barstr);
        stop = !strncmp(&barstr[len - 9], Code39[C39_STOP], 9);
        if (start && stop)
            *pvalid = 1;
        else {
            revbarstr = stringReverse(barstr);
            start = !strncmp(revbarstr, Code39[C39_START], 9);
            stop = !strncmp(&revbarstr[len - 9], Code39[C39_STOP], 9);
            FREE(revbarstr);
            if (start && stop) {
                *pvalid = 1;
                if (preverse) *preverse = 1;
            }
        }
        break;
    case L_BF_CODABAR:
        start = stop = 0;
        len = strlen(barstr);
        for (i = 16; i <= 19; i++)  /* any of these will do */
            start += !strncmp(barstr, Codabar[i], 7);
        for (i = 16; i <= 19; i++)  /* ditto */
            stop += !strncmp(&barstr[len - 7], Codabar[i], 7);
        if (start && stop)
            *pvalid = 1;
        else {
            start = stop = 0;
            revbarstr = stringReverse(barstr);
            for (i = 16; i <= 19; i++)
                start += !strncmp(revbarstr, Codabar[i], 7);
            for (i = 16; i <= 19; i++)
                stop += !strncmp(&revbarstr[len - 7], Codabar[i], 7);
            FREE(revbarstr);
            if (start && stop) {
                *pvalid = 1;
                if (preverse) *preverse = 1;
            }
        }
        break;
    case L_BF_UPCA:
    case L_BF_EAN13:
        len = strlen(barstr);
        if (len == 59) {
            start = !strncmp(barstr, Upca[UPCA_START], 3);
            mid = !strncmp(&barstr[27], Upca[UPCA_MID], 5);
            stop = !strncmp(&barstr[len - 3], Upca[UPCA_STOP], 3);
            if (start && mid && stop)
                *pvalid = 1;
        }
        break;
    default:
        return ERROR_INT("format not supported", procName, 1);
    }

    return 0;
}


/*------------------------------------------------------------------------*
 *                             Code 2 of 5                                *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecode2of5()
 *
 *      Input:  barstr (of widths, in set {1, 2})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref: http://en.wikipedia.org/wiki/Two-out-of-five_code (Note:
 *                 the codes given here are wrong!)
 *               http://morovia.com/education/symbology/code25.asp
 *      (2) This is a very low density encoding for the 10 digits.
 *          Each digit is encoded with 5 black bars, of which 2 are wide
 *          and 3 are narrow.  No information is carried in the spaces
 *          between the bars, which are all equal in width, represented by
 *          a "1" in our encoding.
 *      (3) The mapping from the sequence of five bar widths to the
 *          digit is identical to the mapping used by the interleaved
 *          2 of 5 code.  The start code is 21211, representing two
 *          wide bars and a narrow bar, and the interleaved "1" spaces
 *          are explicit.  The stop code is 21112.  For all codes
 *          (including start and stop), the trailing space "1" is
 *          implicit -- there is no reason to represent it in the
 *          Code2of5[] array.
 */
static char *
barcodeDecode2of5(char    *barstr,
                  l_int32  debugflag)
{
char    *data, *vbarstr;
char     code[10];
l_int32  valid, reverse, i, j, len, error, ndigits, start, found;

    PROCNAME("barcodeDecodeI2of5");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse if necessary */
    barcodeVerifyFormat(barstr, L_BF_CODE2OF5, &valid, &reverse);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in 2of5 format", procName, NULL);
    if (reverse)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Verify size */
    len = strlen(vbarstr);
    if ((len - 11) % 10 != 0)
        return (char *)ERROR_PTR("size not divisible by 10: invalid 2of5 code",
                                 procName, NULL);

    error = FALSE;
    ndigits = (len - 11) / 10;
    data = (char *)CALLOC(ndigits + 1, sizeof(char));
    memset(code, 0, 10);
    for (i = 0; i < ndigits; i++) {
        start = 6 + 10 * i;
        for (j = 0; j < 9; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

	found = FALSE;
        for (j = 0; j < 10; j++) {
            if (!strcmp(code, Code2of5[j])) {
                data[i] = 0x30 + j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

    return data;
}


/*------------------------------------------------------------------------*
 *                       Interleaved Code 2 of 5                          *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecodeI2of5()
 *
 *      Input:  barstr (of widths, in set {1, 2})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref: http://en.wikipedia.org/wiki/Interleaved_2_of_5
 *      (2) This always encodes an even number of digits.
 *          The start code is 1111; the stop code is 211.
 */
static char *
barcodeDecodeI2of5(char    *barstr,
                   l_int32  debugflag)
{
char    *data, *vbarstr;
char     code1[6], code2[6];
l_int32  valid, reverse, i, j, len, error, npairs, start, found;

    PROCNAME("barcodeDecodeI2of5");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse if necessary */
    barcodeVerifyFormat(barstr, L_BF_CODEI2OF5, &valid, &reverse);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in i2of5 format", procName, NULL);
    if (reverse)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Verify size */
    len = strlen(vbarstr);
    if ((len - 7) % 10 != 0)
        return (char *)ERROR_PTR("size not divisible by 10: invalid I2of5 code",
                                 procName, NULL);

    error = FALSE;
    npairs = (len - 7) / 10;
    data = (char *)CALLOC(2 * npairs + 1, sizeof(char));
    memset(code1, 0, 6);
    memset(code2, 0, 6);
    for (i = 0; i < npairs; i++) {
        start = 4 + 10 * i;
        for (j = 0; j < 5; j++) {
            code1[j] = vbarstr[start + 2 * j];
            code2[j] = vbarstr[start + 2 * j + 1];
        }

        if (debugflag)
            fprintf(stderr, "code1: %s, code2: %s\n", code1, code2);

	found = FALSE;
        for (j = 0; j < 10; j++) {
            if (!strcmp(code1, CodeI2of5[j])) {
                data[2 * i] = 0x30 + j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
	found = FALSE;
        for (j = 0; j < 10; j++) {
            if (!strcmp(code2, CodeI2of5[j])) {
                data[2 * i + 1] = 0x30 + j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

    return data;
}


/*------------------------------------------------------------------------*
 *                                 Code 93                                *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecode93()
 *
 *      Input:  barstr (of widths, in set {1, 2, 3, 4})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref:  http://en.wikipedia.org/wiki/Code93
 *                http://morovia.com/education/symbology/code93.asp
 *      (2) Each symbol has 3 black and 3 white bars.
 *          The start and stop codes are 111141; the stop code then is
 *          terminated with a final (1) bar.
 *      (3) The last two codes are check codes.  We are checking them
 *          for correctness, and issuing a warning on failure.  Should
 *          probably not return any data on failure.
 */
static char *
barcodeDecode93(char    *barstr,
                l_int32  debugflag)
{
const char  *checkc, *checkk;
char        *data, *vbarstr;
char         code[7];
l_int32      valid, reverse, i, j, len, error, nsymb, start, found, sum;
l_int32     *index;

    PROCNAME("barcodeDecode93");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse if necessary */
    barcodeVerifyFormat(barstr, L_BF_CODE93, &valid, &reverse);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in code93 format", procName, NULL);
    if (reverse)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Verify size; skip the first 6 and last 7 bars. */
    len = strlen(vbarstr);
    if ((len - 13) % 6 != 0)
        return (char *)ERROR_PTR("size not divisible by 6: invalid code 93",
                                 procName, NULL);

        /* Decode the symbols */
    nsymb = (len - 13) / 6;
    data = (char *)CALLOC(nsymb + 1, sizeof(char));
    index = (l_int32 *)CALLOC(nsymb, sizeof(l_int32));
    memset(code, 0, 7);
    error = FALSE;
    for (i = 0; i < nsymb; i++) {
        start = 6 + 6 * i;
        for (j = 0; j < 6; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

        found = FALSE;
        for (j = 0; j < C93_START; j++) {
            if (!strcmp(code, Code93[j])) {
                data[i] = Code93Val[j];
                index[i] = j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(index);
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

        /* Do check sums.  For character "C", use only the
         * actual data in computing the sum.  For character "K",
         * use the actual data plus the check character "C". */
    sum = 0;
    for (i = 0; i < nsymb - 2; i++)  /* skip the "C" and "K" */
        sum += ((i % 20) + 1) * index[nsymb - 3 - i];
    if (data[nsymb - 2] != Code93Val[sum % 47])
        L_WARNING("Error for check C", procName);

    if (debugflag) {
        checkc = Code93[sum % 47];
        fprintf(stderr, "checkc = %s\n", checkc);
    }

    sum = 0;
    for (i = 0; i < nsymb - 1; i++)  /* skip the "K" */
        sum += ((i % 15) + 1) * index[nsymb - 2 - i];
    if (data[nsymb - 1] != Code93Val[sum % 47])
        L_WARNING("Error for check K", procName);

    if (debugflag) {
        checkk = Code93[sum % 47];
        fprintf(stderr, "checkk = %s\n", checkk);
    }

        /* Remove the two check codes from the output */
    data[nsymb - 2] = '\0';

    FREE(index);
    return data;
}


/*------------------------------------------------------------------------*
 *                                 Code 39                                *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecode39()
 *
 *      Input:  barstr (of widths, in set {1, 2})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref:  http://en.wikipedia.org/wiki/Code39
 *                http://morovia.com/education/symbology/code39.asp
 *      (2) Each symbol has 5 black and 4 white bars.
 *          The start and stop codes are 121121211 (the asterisk)
 *      (3) This decoder was contributed by Roger Hyde.
 */
static char *
barcodeDecode39(char    *barstr,
                l_int32  debugflag)
{
char     *data, *vbarstr;
char      code[10];
l_int32   valid, reverse, i, j, len, error, nsymb, start, found;

    PROCNAME("barcodeDecode39");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse if necessary */
    barcodeVerifyFormat(barstr, L_BF_CODE39, &valid, &reverse);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in code39 format", procName, NULL);
    if (reverse)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Verify size */
    len = strlen(vbarstr);
    if ((len + 1) % 10 != 0)
        return (char *)ERROR_PTR("size+1 not divisible by 10: invalid code 39",
                                 procName, NULL);

        /* Decode the symbols */
    nsymb = (len - 19) / 10;
    data = (char *)CALLOC(nsymb + 1, sizeof(char));
    memset(code, 0, 10);
    error = FALSE;
    for (i = 0; i < nsymb; i++) {
        start = 10 + 10 * i;
        for (j = 0; j < 9; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

        found = FALSE;
        for (j = 0; j < C39_START; j++) {
            if (!strcmp(code, Code39[j])) {
                data[i] = Code39Val[j];
                found = TRUE;
                break;
            }
        }
        if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

    return data;
}


/*------------------------------------------------------------------------*
 *                                 Codabar                                *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecodeCodabar()
 *
 *      Input:  barstr (of widths, in set {1, 2})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref:  http://en.wikipedia.org/wiki/Codabar
 *                http://morovia.com/education/symbology/codabar.asp
 *      (2) Each symbol has 4 black and 3 white bars.  They represent the
 *          10 digits, and optionally 6 other characters.  The start and
 *          stop codes can be any of four (typically denoted A,B,C,D).
 */
static char *
barcodeDecodeCodabar(char    *barstr,
                     l_int32  debugflag)
{
char     *data, *vbarstr;
char      code[8];
l_int32   valid, reverse, i, j, len, error, nsymb, start, found;

    PROCNAME("barcodeDecodeCodabar");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse if necessary */
    barcodeVerifyFormat(barstr, L_BF_CODABAR, &valid, &reverse);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in codabar format",
                                 procName, NULL);
    if (reverse)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Verify size */
    len = strlen(vbarstr);
    if ((len + 1) % 8 != 0)
        return (char *)ERROR_PTR("size+1 not divisible by 8: invalid codabar",
                                 procName, NULL);

        /* Decode the symbols */
    nsymb = (len - 15) / 8;
    data = (char *)CALLOC(nsymb + 1, sizeof(char));
    memset(code, 0, 8);
    error = FALSE;
    for (i = 0; i < nsymb; i++) {
        start = 8 + 8 * i;
        for (j = 0; j < 7; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

        found = FALSE;
        for (j = 0; j < 16; j++) {
            if (!strcmp(code, Codabar[j])) {
                data[i] = CodabarVal[j];
                found = TRUE;
                break;
            }
        }
        if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

    return data;
}


/*------------------------------------------------------------------------*
 *                               Code UPC-A                               *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecodeUpca()
 *
 *      Input:  barstr (of widths, in set {1, 2, 3, 4})
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
 *                http://morovia.com/education/symbology/upc-a.asp
 *      (2) Each symbol has 2 black and 2 white bars, and encodes a digit.
 *          The start and stop codes are 111 and 111.  There are a total of
 *          30 black bars, encoding 12 digits in two sets of 6, with
 *          2 black bars separating the sets.
 *      (3) The last digit is a check digit.  We check for correctness, and
 *          issue a warning on failure.  Should probably not return any
 *          data on failure.
 */
static char *
barcodeDecodeUpca(char    *barstr,
                  l_int32  debugflag)
{
char     *data, *vbarstr;
char      code[5];
l_int32   valid, i, j, len, error, start, found, sum, checkdigit;

    PROCNAME("barcodeDecodeUpca");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format; reverse has no meaning here -- we must test both */
    barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in UPC-A format", procName, NULL);

        /* Verify size */
    len = strlen(barstr);
    if (len != 59)
        return (char *)ERROR_PTR("size not 59; invalid UPC-A barcode",
                                 procName, NULL);

        /* Check the first digit.  If invalid, reverse the string. */
    memset(code, 0, 5);
    for (i = 0; i < 4; i++)
        code[i] = barstr[i + 3];
    found = FALSE;
    for (i = 0; i < 10; i++) {
        if (!strcmp(code, Upca[i])) {
            found = TRUE;
            break;
        }
    }
    if (found == FALSE)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Decode the 12 symbols */
    data = (char *)CALLOC(13, sizeof(char));
    memset(code, 0, 5);
    error = FALSE;
    for (i = 0; i < 12; i++) {
        if (i < 6)
            start = 3 + 4 * i;
        else
            start = 32 + 4 * (i - 6);
        for (j = 0; j < 4; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

        found = FALSE;
        for (j = 0; j < 10; j++) {
            if (!strcmp(code, Upca[j])) {
                data[i] = 0x30 + j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

        /* Calculate the check digit (data[11]). */
    sum = 0;
    for (i = 0; i < 12; i += 2)  /* "even" digits */
        sum += 3 * (data[i] - 0x30);
    for (i = 1; i < 11; i += 2)  /* "odd" digits */
        sum += (data[i] - 0x30);
    checkdigit = sum % 10;
    if (checkdigit)  /* not 0 */
        checkdigit = 10 - checkdigit;
    if (checkdigit + 0x30 != data[11])
        L_WARNING("Error for UPC-A check character", procName);

    return data;
}


/*------------------------------------------------------------------------*
 *                               Code EAN-13                              *
 *------------------------------------------------------------------------*/
/*!
 *  barcodeDecodeEan13()
 *
 *      Input:  barstr (of widths, in set {1, 2, 3, 4})
 *              first (first digit: 0 - 9)
 *              debugflag
 *      Return: data (string of digits), or null if none found or on error
 *
 *  Notes:
 *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
 *                http://morovia.com/education/symbology/ean-13.asp
 *      (2) The encoding is essentially the same as UPC-A, except
 *          there are 13 digits in total, of which 12 are encoded
 *          by bars (as with UPC-A) and the 13th is a leading digit
 *          that determines the encoding of the next 6 digits,
 *          selecting each digit from one of two tables.
 *          encoded in the bars (as with UPC-A).  If the first digit
 *          is 0, the encoding is identical to UPC-A.
 *      (3) As with UPC-A, the last digit is a check digit.
 *      (4) For now, we assume the first digit is input to this function.
 *          Eventually, we will read it by pattern matching.
 *
 *    TODO: fix this for multiple tables, depending on the value of @first
 */
static char *
barcodeDecodeEan13(char    *barstr,
                   l_int32  first,
		   l_int32  debugflag)
{
char     *data, *vbarstr;
char      code[5];
l_int32   valid, i, j, len, error, start, found, sum, checkdigit;

    PROCNAME("barcodeDecodeEan13");

    if (!barstr)
        return (char *)ERROR_PTR("barstr not defined", procName, NULL);

        /* Verify format.  You can't tell the orientation by the start
	 * and stop codes, but you can by the location of the digits.
	 * Use the UPCA verifier for EAN 13 -- it is identical. */
    barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL);
    if (!valid)
        return (char *)ERROR_PTR("barstr not in EAN 13 format", procName, NULL);

        /* Verify size */
    len = strlen(barstr);
    if (len != 59)
        return (char *)ERROR_PTR("size not 59; invalid EAN 13 barcode",
                                 procName, NULL);

        /* Check the first digit.  If invalid, reverse the string. */
    memset(code, 0, 5);
    for (i = 0; i < 4; i++)
        code[i] = barstr[i + 3];
    found = FALSE;
    for (i = 0; i < 10; i++) {
        if (!strcmp(code, Upca[i])) {
            found = TRUE;
            break;
        }
    }
    if (found == FALSE)
        vbarstr = stringReverse(barstr);
    else
        vbarstr = stringNew(barstr);

        /* Decode the 12 symbols */
    data = (char *)CALLOC(13, sizeof(char));
    memset(code, 0, 5);
    error = FALSE;
    for (i = 0; i < 12; i++) {
        if (i < 6)
            start = 3 + 4 * i;
        else
            start = 32 + 4 * (i - 6);
        for (j = 0; j < 4; j++)
            code[j] = vbarstr[start + j];

        if (debugflag)
            fprintf(stderr, "code: %s\n", code);

        found = FALSE;
        for (j = 0; j < 10; j++) {
            if (!strcmp(code, Upca[j])) {
                data[i] = 0x30 + j;
		found = TRUE;
                break;
            }
        }
	if (!found) error = TRUE;
    }
    FREE(vbarstr);

    if (error) {
        FREE(data);
        return (char *)ERROR_PTR("error in decoding", procName, NULL);
    }

        /* Calculate the check digit (data[11]). */
    sum = 0;
    for (i = 0; i < 12; i += 2)  /* "even" digits */
        sum += 3 * (data[i] - 0x30);
    for (i = 1; i < 12; i += 2)  /* "odd" digits */
        sum += (data[i] - 0x30);
    checkdigit = sum % 10;
    if (checkdigit)  /* not 0 */
        checkdigit = 10 - checkdigit;
    if (checkdigit + 0x30 != data[11])
        L_WARNING("Error for EAN-13 check character", procName);

    return data;
}


