/*
 * library.c -- external functions of libbarcode
 *
 * Copyright (c) 1999 Alessandro Rubini (rubini@prosa.it)
 * Copyright (c) 1999 Prosa Srl. (prosa@prosa.it)
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>

#include "barcode.h"

/*
 * This function allocates a barcode structure and strdup()s the
 * text string. It returns NULL in case of error
 */
struct Barcode_Item *Barcode_Create(char *text)
{
    struct Barcode_Item *bc;

    bc = malloc(sizeof(*bc));
    if (!bc) return NULL;

    memset(bc, 0, sizeof(*bc));
    bc->ascii = strdup(text);
    bc->margin = BARCODE_DEFAULT_MARGIN; /* default margin */
    return bc;
}


/*
 * Free a barcode structure
 */
int Barcode_Delete(struct Barcode_Item *bc)
{
    if (bc->ascii)
	free(bc->ascii);
    if (bc->partial)
	free(bc->partial);
    if (bc->textinfo)
	free(bc->textinfo);
    if (bc->encoding)
	free(bc->encoding);
    free(bc);
    return 0; /* always success */
}


/*
 * The various supported encodings.  This might be extended to support
 * dynamic addition of extra encodings
 */
extern int Barcode_ean_verify(char *text);
extern int Barcode_ean_encode(struct Barcode_Item *bc);
extern int Barcode_upc_verify(char *text);
extern int Barcode_upc_encode(struct Barcode_Item *bc);
extern int Barcode_isbn_verify(char *text);
extern int Barcode_isbn_encode(struct Barcode_Item *bc);
extern int Barcode_39_verify(char *text);
extern int Barcode_39_encode(struct Barcode_Item *bc);
#if 0 /* not yet implemented */
extern int Barcode_128_verify(char *text);
extern int Barcode_128_encode(struct Barcode_Item *bc);
extern int Barcode_128c_verify(char *text);
extern int Barcode_128c_encode(struct Barcode_Item *bc);
#endif

struct encoding {
    int type;
    int (*verify)(char *text);
    int (*encode)(struct Barcode_Item *bc);
};

struct encoding encodings[] = {
    {BARCODE_EAN,  Barcode_ean_verify,  Barcode_ean_encode},
    {BARCODE_UPC,  Barcode_upc_verify,  Barcode_upc_encode},
    {BARCODE_ISBN, Barcode_isbn_verify, Barcode_isbn_encode},
    {BARCODE_39,   Barcode_39_verify,   Barcode_39_encode},
#if 0 /* not yet implemented */
    {BARCODE_128,  Barcode_128_verify,  Barcode_128_encode},
    {BARCODE_128C, Barcode_128_verify,  Barcode_128_encode},
#endif
    {0,            NULL,                NULL}
};

/*
 * A function to encode a string into bc->partial, ready for
 * postprocessing to the output file. Meaningful bits for "flags" are
 * the encoding mask, the noascii bit and stretch bit. These bits
 * get saved in the data structure.
 */
int Barcode_Encode(struct Barcode_Item *bc, int flags)
{
    int validbits = BARCODE_ENCODING_MASK | BARCODE_NO_CHECKSUM;
    struct encoding *cptr;

    /* If any flag is cleared in "flags", inherit it from "bc->flags" */
    if (!(flags & BARCODE_ENCODING_MASK))
	flags |= bc->flags & BARCODE_ENCODING_MASK;
    flags = bc->flags = (flags & validbits) | (bc->flags & ~validbits);

    if (!(flags & BARCODE_ENCODING_MASK)) {
	/* get the first code able to handle the text */
	for (cptr = encodings; cptr->verify; cptr++)
	    if (cptr->verify(bc->ascii)==0)
		break;
	if (!cptr->verify) {
	    bc->error = EINVAL; /* no code can handle this text */
	    return -1;
	}
	flags |= cptr->type; /* this works */
	bc->flags |= cptr->type;
    }
    for (cptr = encodings; cptr->verify; cptr++)
	if (cptr->type == (flags & BARCODE_ENCODING_MASK))
	    break;
    if (!cptr->verify) {
	bc->error = EINVAL; /* invalid barcode type */
	return -1;
    }
    if (cptr->verify(bc->ascii) != 0) {
	bc->error = EINVAL;
	return -1;
    }
    return cptr->encode(bc);
}


/* 
 * When multiple output formats are supported, there will
 * be a jumpt table like the one for the types. Now we don't need it
 */
extern int Barcode_ps_print(struct Barcode_Item *bc, FILE *f);

/*
 * A function to print a partially decoded string. Meaningful bits for
 * "flags" are the output mask. These bits get saved in the data
 * structure. 
 */
int Barcode_Print(struct Barcode_Item *bc, FILE *f, int flags)
{
    int validbits = BARCODE_OUTPUT_MASK | BARCODE_NO_ASCII
	| BARCODE_OUT_NOHEADERS;

    /* If any flag is clear in "flags", inherit it from "bc->flags" */
    if (!(flags & BARCODE_OUTPUT_MASK))
	flags |= bc->flags & BARCODE_OUTPUT_MASK;
    if (!(flags & BARCODE_NO_ASCII))
	flags |= bc->flags & BARCODE_NO_ASCII;
    if (!(flags & BARCODE_OUT_NOHEADERS))
	flags |= bc->flags & BARCODE_OUT_NOHEADERS;
    flags = bc->flags = (flags & validbits) | (bc->flags & ~validbits);

    return Barcode_ps_print(bc, f);
}

/*
 * Choose the position
 */
int Barcode_Position(struct Barcode_Item *bc, int wid, int hei,
		     int xoff, int yoff, double scalef)
{
    bc->width = wid; bc->height = hei;
    bc->xoff = xoff; bc->yoff = yoff;
    bc->scalef = scalef;
    return 0;
}

/*
 * Do it all in one step
 */
int Barcode_Encode_and_Print(char *text, FILE *f, int wid, int hei,
				    int xoff, int yoff, int flags)
{
    struct Barcode_Item * bc;
    
    if (!(bc=Barcode_Create(text))) {
	errno = -ENOMEM;
	return -1;
    }
    if (     Barcode_Position(bc, wid, hei, xoff, yoff, 0.0) < 0
	  || Barcode_Encode(bc, flags) < 0
	  || Barcode_Print(bc, f, flags) < 0) {
	errno = bc->error;
	Barcode_Delete(bc);
	return -1;
    }
    Barcode_Delete(bc);
    return 0;
}

/*
 * Return the version
 */

int Barcode_Version(char *vptr)
{
    if (vptr)
	strcpy(vptr, BARCODE_VERSION);
    return BARCODE_VERSION_INT;
}
