/*------------------------------------------------------------------------
 *  Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>
 *
 *  This file is part of the ZBar Bar Code Reader.
 *
 *  The ZBar Bar Code Reader is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Lesser Public License as
 *  published by the Free Software Foundation; either version 2.1 of
 *  the License, or (at your option) any later version.
 *
 *  The ZBar Bar Code Reader 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 Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser Public License
 *  along with the ZBar Bar Code Reader; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 *  Boston, MA  02110-1301  USA
 *
 *  http://sourceforge.net/projects/zbar
 *------------------------------------------------------------------------*/
#ifndef _DECODER_H_
#define _DECODER_H_

#include <config.h>
#include <stdlib.h>     /* realloc */
#include <limits.h>

#include <zbar.h>

#include "debug.h"

#define NUM_CFGS (ZBAR_CFG_MAX_LEN - ZBAR_CFG_MIN_LEN + 1)

#if ENABLE_EAN == 1
# include "decoder/ean.h"
#endif
#if ENABLE_I25 == 1
# include "decoder/i25.h"
#endif
#if ENABLE_DATABAR == 1
# include "decoder/databar.h"
#endif
#if ENABLE_CODABAR == 1
# include "decoder/codabar.h"
#endif
#if ENABLE_CODE39 == 1
# include "decoder/code39.h"
#endif
#if ENABLE_CODE93 == 1
# include "decoder/code93.h"
#endif
#if ENABLE_CODE128 == 1
# include "decoder/code128.h"
#endif
#if ENABLE_PDF417 == 1
# include "decoder/pdf417.h"
#endif
#if ENABLE_QRCODE == 1
# include "decoder/qr_finder.h"
#endif
#if ENABLE_SQCODE == 1
# include "decoder/sq_finder.h"
#endif

/* size of bar width history (implementation assumes power of two) */
#ifndef DECODE_WINDOW
# define DECODE_WINDOW  16
#endif

/* initial data buffer allocation */
#ifndef BUFFER_MIN
# define BUFFER_MIN   0x20
#endif

/* maximum data buffer allocation
 * (longer symbols are rejected)
 */
#ifndef BUFFER_MAX
# define BUFFER_MAX  0x100
#endif

/* buffer allocation increment */
#ifndef BUFFER_INCR
# define BUFFER_INCR  0x10
#endif

#define CFG(dcode, cfg) ((dcode).configs[(cfg) - ZBAR_CFG_MIN_LEN])
#define TEST_CFG(config, cfg) (((config) >> (cfg)) & 1)
#define MOD(mod) (1 << (mod))

/* symbology independent decoder state */
struct zbar_decoder_s {
    unsigned char idx;                  /* current width index */
    unsigned w[DECODE_WINDOW];          /* window of last N bar widths */
    zbar_symbol_type_t type;            /* type of last decoded data */
    zbar_symbol_type_t lock;            /* buffer lock */
    unsigned modifiers;                 /* symbology modifier */
    int direction;                      /* direction of last decoded data */
    unsigned s6;                        /* 6-element character width */

    /* everything above here is automatically reset */
    unsigned buf_alloc;                 /* dynamic buffer allocation */
    unsigned buflen;                    /* binary data length */
    unsigned char *buf;                 /* decoded characters */
    void *userdata;                     /* application data */
    zbar_decoder_handler_t *handler;    /* application callback */

    /* symbology specific state */
#if ENABLE_EAN == 1
    ean_decoder_t ean;                  /* EAN/UPC parallel decode attempts */
#endif
#if ENABLE_I25 == 1
    i25_decoder_t i25;                  /* Interleaved 2 of 5 decode state */
#endif
#if ENABLE_DATABAR == 1
    databar_decoder_t databar;          /* DataBar decode state */
#endif
#if ENABLE_CODABAR == 1
    codabar_decoder_t codabar;          /* Codabar decode state */
#endif
#if ENABLE_CODE39 == 1
    code39_decoder_t code39;            /* Code 39 decode state */
#endif
#if ENABLE_CODE93 == 1
    code93_decoder_t code93;            /* Code 93 decode state */
#endif
#if ENABLE_CODE128 == 1
    code128_decoder_t code128;          /* Code 128 decode state */
#endif
#if ENABLE_PDF417 == 1
    pdf417_decoder_t pdf417;            /* PDF417 decode state */
#endif
#if ENABLE_QRCODE == 1
    qr_finder_t qrf;                    /* QR Code finder state */
#endif
#if ENABLE_SQCODE == 1
    sq_finder_t sqf;                    /* SQ Code finder state */
#endif
};

/* return current element color */
static inline char get_color (const zbar_decoder_t *dcode)
{
    return(dcode->idx & 1);
}

/* retrieve i-th previous element width */
static inline unsigned get_width (const zbar_decoder_t *dcode,
                                  unsigned char offset)
{
    return(dcode->w[(dcode->idx - offset) & (DECODE_WINDOW - 1)]);
}

/* retrieve bar+space pair width starting at offset i */
static inline unsigned pair_width (const zbar_decoder_t *dcode,
                                   unsigned char offset)
{
    return(get_width(dcode, offset) + get_width(dcode, offset + 1));
}

/* calculate total character width "s"
 *   - start of character identified by context sensitive offset
 *     (<= DECODE_WINDOW - n)
 *   - size of character is n elements
 */
static inline unsigned calc_s (const zbar_decoder_t *dcode,
                               unsigned char offset,
                               unsigned char n)
{
    /* FIXME check that this gets unrolled for constant n */
    unsigned s = 0;
    while(n--)
        s += get_width(dcode, offset++);
    return(s);
}

/* fixed character width decode assist
 * bar+space width are compared as a fraction of the reference dimension "x"
 *   - +/- 1/2 x tolerance
 *   - measured total character width (s) compared to symbology baseline (n)
 *     (n = 7 for EAN/UPC, 11 for Code 128)
 *   - bar+space *pair width* "e" is used to factor out bad "exposures"
 *     ("blooming" or "swelling" of dark or light areas)
 *     => using like-edge measurements avoids these issues
 *   - n should be > 3
 */
static inline int decode_e (unsigned e,
                            unsigned s,
                            unsigned n)
{
    /* result is encoded number of units - 2
     * (for use as zero based index)
     * or -1 if invalid
     */
    unsigned char E = ((e * n * 2 + 1) / s - 3) / 2;
    return((E >= n - 3) ? -1 : E);
}

/* sort three like-colored elements and return ordering
 */
static inline unsigned decode_sort3 (zbar_decoder_t *dcode,
                                     int i0)
{
    unsigned w0 = get_width(dcode, i0);
    unsigned w2 = get_width(dcode, i0 + 2);
    unsigned w4 = get_width(dcode, i0 + 4);
    if(w0 < w2) {
        if(w2 < w4)
            return((i0 << 8) | ((i0 + 2) << 4) | (i0 + 4));
        if(w0 < w4)
            return((i0 << 8) | ((i0 + 4) << 4) | (i0 + 2));
        return(((i0 + 4) << 8) | (i0 << 4) | (i0 + 2));
    }
    if(w4 < w2)
        return(((i0 + 4) << 8) | ((i0 + 2) << 4) | i0);
    if(w0 < w4)
        return(((i0 + 2) << 8) | (i0 << 4) | (i0 + 4));
    return(((i0 + 2) << 8) | ((i0 + 4) << 4) | i0);
}

/* sort N like-colored elements and return ordering
 */
static inline unsigned decode_sortn (zbar_decoder_t *dcode,
                                     int n,
                                     int i0)
{
    unsigned mask = 0, sort = 0;
    int i;
    for(i = n - 1; i >= 0; i--) {
        unsigned wmin = UINT_MAX;
        int jmin = -1, j;
        for(j = n - 1; j >= 0; j--) {
            if((mask >> j) & 1)
                continue;
            unsigned w = get_width(dcode, i0 + j * 2);
            if(wmin >= w) {
                wmin = w;
                jmin = j;
            }
        }
        zassert(jmin >= 0, 0, "sortn(%d,%d) jmin=%d",
                n, i0, jmin);
        sort <<= 4;
        mask |= 1 << jmin;
        sort |= i0 + jmin * 2;
    }
    return(sort);
}

/* acquire shared state lock */
static inline char acquire_lock (zbar_decoder_t *dcode,
                                 zbar_symbol_type_t req)
{
    if(dcode->lock) {
        dbprintf(2, " [locked %d]\n", dcode->lock);
        return(1);
    }
    dcode->lock = req;
    return(0);
}

/* check and release shared state lock */
static inline char release_lock (zbar_decoder_t *dcode,
                                 zbar_symbol_type_t req)
{
    zassert(dcode->lock == req, 1, "lock=%d req=%d\n",
            dcode->lock, req);
    dcode->lock = 0;
    return(0);
}

/* ensure output buffer has sufficient allocation for request */
static inline char size_buf (zbar_decoder_t *dcode,
                             unsigned len)
{
    unsigned char *buf;
    if(len <= BUFFER_MIN)
        return(0);
    if(len < dcode->buf_alloc)
        /* FIXME size reduction heuristic? */
        return(0);
    if(len > BUFFER_MAX)
        return(1);
    if(len < dcode->buf_alloc + BUFFER_INCR) {
        len = dcode->buf_alloc + BUFFER_INCR;
        if(len > BUFFER_MAX)
            len = BUFFER_MAX;
    }
    buf = realloc(dcode->buf, len);
    if(!buf)
        return(1);
    dcode->buf = buf;
    dcode->buf_alloc = len;
    return(0);
}

extern const char *_zbar_decoder_buf_dump (unsigned char *buf,
                                           unsigned int buflen);

#endif
