/*
 * code128.c -- encoding for code128 (A, B, C)
 *
 * Copyright (c) 1999,2000 Alessandro Rubini (rubini@gnu.org)
 * Copyright (c) 1999 Prosa Srl. (prosa@prosa.it)
 * Copyright (c) 2000 Leonid A. Broukhis (leob@mailcom.com)
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

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

#include "barcode.h"

static char *codeset[] = {
    "212222", "222122", "222221", "121223", "121322",  /*  0 -  4 */
    "131222", "122213", "122312", "132212", "221213",
    "221312", "231212", "112232", "122132", "122231",  /* 10 - 14 */
    "113222", "123122", "123221", "223211", "221132",
    "221231", "213212", "223112", "312131", "311222",  /* 20 - 24 */
    "321122", "321221", "312212", "322112", "322211",
    "212123", "212321", "232121", "111323", "131123",  /* 30 - 34 */
    "131321", "112313", "132113", "132311", "211313",
    "231113", "231311", "112133", "112331", "132131",  /* 40 - 44 */
    "113123", "113321", "133121", "313121", "211331",
    "231131", "213113", "213311", "213131", "311123",  /* 50 - 54 */
    "311321", "331121", "312113", "312311", "332111",
    "314111", "221411", "431111", "111224", "111422",  /* 60 - 64 */
    "121124", "121421", "141122", "141221", "112214",
    "112412", "122114", "122411", "142112", "142211",  /* 70 - 74 */
    "241211", "221114", "413111", "241112", "134111",
    "111242", "121142", "121241", "114212", "124112",  /* 80 - 84 */
    "124211", "411212", "421112", "421211", "212141",
    "214121", "412121", "111143", "111341", "131141",  /* 90 - 94 */
    "114113", "114311", "411113", "411311", "113141",
    "114131", "311141", "411131", "b1a4a2", "b1a2a4",  /* 100 - 104 */
    "b1a2c2", "b3c1a1b"
};

#define START_A 103
#define START_B 104
#define START_C 105
#define STOP    106
#define SHIFT    98 /* only A and B */
#define CODE_A  101 /* only B and C */
#define CODE_B  100 /* only A and C */
#define CODE_C   99 /* only A and B */
#define FUNC_1  102 /* all of them */
#define FUNC_2   97 /* only A and B */
#define FUNC_3   96 /* only A and B */
/* FUNC_4 is CODE_A when in A and CODE_B when in B */

#define SYMBOL_WID 11 /* all of them are 11-bar wide */

/*
 * code 128-b includes all printable ascii chars
 */

int Barcode_128b_verify(char *text)
{
    if (!strlen(text))
	return -1;
    while (*text && *text>=32 && !(*text&0x80))
	text++;
    if (*text)
	return -1; /* a non-ascii char */
    return 0; /* ok */
}

int Barcode_128b_encode(struct Barcode_Item *bc)
{
    static char *text;
    static char *partial;  /* dynamic */
    static char *textinfo; /* dynamic */
    char *textptr;
    int i, code, textpos, checksum = 0;

    if (bc->partial)
	free(bc->partial);
    if (bc->textinfo)
	free(bc->textinfo);
    bc->partial = bc->textinfo = NULL; /* safe */

    if (!bc->encoding)
	bc->encoding = strdup("code 128-B");

    text = bc->ascii;
    if (!text) {
        bc->error = EINVAL;
        return -1;
    }
    /* the partial code is 6* (head + text + check + tail) + final + term. */
    partial = malloc( (strlen(text) + 4) * 6 +2);
    if (!partial) {
        bc->error = errno;
        return -1;
    }

    /* the text information is at most "nnn:fff:c " * strlen +term */
    textinfo = malloc(10*strlen(text) + 2);
    if (!textinfo) {
        bc->error = errno;
        free(partial);
        return -1;
    }

    /* up to now, it was the same code as other encodings */

    strcpy(partial, "0"); /* the first space */
    strcat(partial, codeset[START_B]);
    checksum += START_B; /* the start char is counted in the checksum */
    textptr = textinfo;
    textpos = SYMBOL_WID;

    for (i=0; i<strlen(text); i++) {
        if ( text[i] < 32 || (text[i] & 0x80)) {
            bc->error = EINVAL; /* impossible if text is verified */
            free(partial);
            free(textinfo);
            return -1;
        }
	code = text[i]-32;
	strcat(partial, codeset[code]);
	checksum += code * (i+1); /* first * 1 + second * 2 + third * 3... */
        sprintf(textptr, "%i:12:%c ", textpos, text[i]);
        textptr += strlen(textptr);
        textpos += SYMBOL_WID; /* width of each code */
    }
    /* Add the checksum, independent of BARCODE_NO_CHECKSUM */
    checksum %= 103;
    strcat(partial, codeset[checksum]);
    /* and the end marker */
    strcat(partial, codeset[STOP]);

    bc->partial = partial;
    bc->textinfo = textinfo;

    return 0;
}

/*
 * code 128-c is only digits, but two per symbol
 */

int Barcode_128c_verify(unsigned char *text)
{
    if (!strlen(text))
	return -1;
    /* must be an even number of digits */
    if (strlen(text)%2)
	return -1;
    /* and must be all digits */
    for (; *text; text++)
	if (!isdigit(*text))
	    return -1;
    return 0;
}

int Barcode_128c_encode(struct Barcode_Item *bc)
{
    static char *text;
    static char *partial;  /* dynamic */
    static char *textinfo; /* dynamic */
    char *textptr;
    int i, code, textpos, checksum = 0;

    if (bc->partial)
	free(bc->partial);
    if (bc->textinfo)
	free(bc->textinfo);
    bc->partial = bc->textinfo = NULL; /* safe */

    if (!bc->encoding)
	bc->encoding = strdup("code 128-C");

    text = bc->ascii;
    if (!text) {
        bc->error = EINVAL;
        return -1;
    }
    /* the partial code is 6* (head + text + check + tail) + final + term. */
    partial = malloc( (strlen(text) + 3) * 6 +2);
    if (!partial) {
        bc->error = errno;
        return -1;
    }

    /* the text information is at most "nnn.5:fff:c " * strlen +term */
    textinfo = malloc(12*strlen(text) + 2);
    if (!textinfo) {
        bc->error = errno;
        free(partial);
        return -1;
    }

    strcpy(partial, "0"); /* the first space */
    strcat(partial, codeset[START_C]);
    checksum += START_C; /* the start char is counted in the checksum */
    textptr = textinfo;
    textpos = SYMBOL_WID;

    for (i=0; i<strlen(text); i+=2) {
        if (!isdigit(text[i]) || !isdigit(text[i+1])) {
            bc->error = EINVAL; /* impossible if text is verified */
            free(partial);
            free(textinfo);
            return -1;
        }
        code = (text[i]-'0') * 10 + text[i+1]-'0';
	strcat(partial, codeset[code]);
	checksum += code * (i/2+1); /* first * 1 + second * 2 + third * 3... */

	/* print as "%s", because we have ".5" positions */
        sprintf(textptr, "%g:9:%c %g:9:%c ", (double)textpos, text[i],
		textpos + (double)SYMBOL_WID/2,	text[i+1]);
        textptr += strlen(textptr);
        textpos += SYMBOL_WID; /* width of each code */
    }
    /* Add the checksum, independent of BARCODE_NO_CHECKSUM */
    checksum %= 103;
    strcat(partial, codeset[checksum]);
    /* and the end marker */
    strcat(partial, codeset[STOP]);

    bc->partial = partial;
    bc->textinfo = textinfo;

    return 0;
}

/*
 * generic (full-featured) code128 implementation: it selects between
 * A, B, C according to the data being encoded. F1, F2, F3, F4 are expressed
 * using ascii chars 0xc1, 0xc2, 0xc3, 0xc4 (0301, 0302, 0303, 0304).
 * Char '\0' is expressed by 0x80 (0200).
 */

int Barcode_128_verify(unsigned char *text)
{
    if (!strlen(text))
	return -1;
    while (*text && (*text<=0x80 || (*text >= 0xc1 && *text <= 0xc4)))
	text++;
    if (*text)
	return -1; /* unencodable character */
    return 0; /* ok */
}


/*
 * These functions are extracted from Barcode_128_encode for clarity.
 * It deals with choosing the symbols used to represent the text
 * and returns a dynamic array of integers, terminated by -1.
 *
 * The algorithm used in choosing the codes comes from App 2 of
 * "El Codigo Estandar EAN/UCC 128", courtesy of AECOC, Spain.
 * Thanks to Dani Pardo for getting permission and giving me a copy
 * of the document
 */

#define NEED_CODE_A(c) ((c)<32 || (c)==0x80) 
#define NEED_CODE_B(c) ((c)>=96 && (c)<128)

static int Barcode_a_or_b(unsigned char *text)
{
    for ( ; *text; text++) {
	if (NEED_CODE_A(*text))
	    return 'A';
	if (NEED_CODE_B(*text))
	    return 'B';
    }
    return 0; /* any */
}

/* code is either 'A' or 'B', and value must be valid */
static int Barcode_encode_as(int code, int value)
{

    /* first check the special chars */
    if (value == 0xC1) return FUNC_1;
    if (value == 0xC2) return FUNC_2;
    if (value == 0xC3) return FUNC_3;
    if (value == 0xC4) { /* F4 */
	if (code == 'A') return CODE_A;
	return CODE_B;
    }

    /* then check ascii values */
    if (value >= 0x20 && value <= 0x5F)
	return value - 0x20; /* both codes */
    if (value == 0x80) return 64; /* code A */
    if (value < 0x20) return value+64; /* code A */
    if (value >= 0x60) return value - 0x20; /* code B */
    /* can't happen */
    return -1;
}

static int *Barcode_128_make_array(struct Barcode_Item *bc, int *lptr)
{
    int len, *codes;
    unsigned char *s;
    int i=0, j, code, checksum;

    /* allocate twice the text length + 5, as this is the worst case */
    len = 2 * strlen(bc->ascii) + 5;
    codes = malloc(len * sizeof(int));
    s = bc->ascii;

    /* choose the starting code */
    if (s[2]=='\0' && isdigit(s[0]) && isdigit(s[1])) {
      code = 'C';
    } else if (isdigit(s[0])&&isdigit(s[1]) && isdigit(s[2])&&isdigit(s[3])) {
	code = 'C';
    } else {
	code = Barcode_a_or_b(s);
	if (!code) code = 'B'; /* default */
    }
    codes[i++] = START_A + code - 'A';
    
    for (s = bc->ascii; *s; /* increments are in the loop */) {
	switch(code) {

	    case 'C':
		if (s[0] == 0xC1) { /* F1 is valid */
		    codes[i++] = FUNC_1;
		    s++;
		} else if (isdigit(s[0]) && isdigit(s[1])) {
		    /* encode two digits */
		    codes[i++] = (s[0]-'0') * 10 + s[1]-'0';
		    s += 2;
		} else {
		    /* change code */
		    code = Barcode_a_or_b(s);
		    if (!code) code = 'B';
		    codes[i++] = (code == 'A') ? CODE_A : CODE_B;
		}
		break;
		    
	    case 'B':
	    case 'A':
		for (j=0; isdigit(s[j]); j++)
		    ;
		if (j>=4) { /* if there are 4 or more digits, turn to C */
		    if (j&1) {
			/* odd number: encode one first */
			codes[i++] = *(s++) - ' ';
		    }
		    codes[i++] = CODE_C;
		    code = 'C';
		} else if (code == 'A' && NEED_CODE_B(*s)) {
		    /* check whether we should use SHIFT or change code */
		    j = Barcode_a_or_b(s+1);
		    if (j == 'B') {
			codes[i++] = CODE_B;
			code = 'B';
		    } else {
			codes[i++] = SHIFT;
			codes[i++] = Barcode_encode_as('B', *s);
			s++;
		    }
		} else if (code == 'B' && NEED_CODE_A(*s)) {
		    /* check whether we should use SHIFT or change code */
		    j = Barcode_a_or_b(s+1);
		    if (j == 'A') {
			codes[i++] = CODE_A;
			code = 'A';
		    } else {
			codes[i++] = SHIFT;
			codes[i++] = Barcode_encode_as('A', *s);
			s++;
		    }
		} else {
		    codes[i++] = Barcode_encode_as(code, *s);
		    s++;
		}
		break;
	}	    
    }
    /* add the checksum */
    checksum = codes[0];
    for (j=1; j<i; j++)
	checksum += j * codes[j];
    checksum %= 103;
    codes[i++] = checksum;
    codes[i++] = STOP;
    if (i > len) {
	fprintf(stderr, "impossible: length is > allocated len\n");
    }
    *lptr = i;
    return codes;
}

/*
 * Encoding is tricky, in that we cannot print the ascii
 * representation in an easy way. Therefore, just skip non-printable
 * chars and try to fit the rest somehow.
 */
int Barcode_128_encode(struct Barcode_Item *bc)
{
    static char *text;
    static char *partial;  /* dynamic */
    static char *textinfo; /* dynamic */
    char *textptr;
    int *codes; /* dynamic */
    int i, c, len;
    double textpos, size, step;

    if (bc->partial)
	free(bc->partial);
    if (bc->textinfo)
	free(bc->textinfo);
    bc->partial = bc->textinfo = NULL; /* safe */

    if (!bc->encoding)
	bc->encoding = strdup("code 128");

    text = bc->ascii;
    if (!text) {
        bc->error = EINVAL;
        return -1;
    }

    codes = Barcode_128_make_array(bc, &len);
    if (!codes) return -1;

    /* the partial code is 6*codelen + ini + term (+margin) */
    partial = malloc( 6 * len + 4);
    if (!partial) {
        bc->error = errno;
	free(codes);
        return -1;
    }

    /* the text information is at most "nnn.n:ff.f:c " * strlen +term */
    textptr = textinfo = malloc(13*strlen(text) + 2);
    if (!textinfo) {
        bc->error = errno;
        free(partial);
	free(codes);
        return -1;
    }

    /* up to now, it was almost the same code as other encodings */

    strcpy(partial, "0"); /* the first space */
    for (i=0; i<len; i++) /* the symbols */
	strcat(partial, codeset[codes[i]]);

    /* avoid bars that fall lower than other bars */
    for (i=0; partial[i]; i++)
	if (isalpha(partial[i]))
	    partial[i] += '1' - 'a';

    /*
     * the accompanying text: reserve a space for every char.
     * F[1-4] are rendered as spaces (separators), other unprintable chars
     * are rendered as underscoress (placeholders)
     */

    /*
     * A size of 9 is good for code-C (used above), so 18 for each symbol,
     * but we place an upper limit of 12 to avoid overlapping on the bars.
     */
    size = (int)(180.0 * (len-3) / strlen(text) + .5) / 10.0;
    if (size > 12.0) size = 12.0;

    /* align the text to the left, using its natural size */
    step = (int)(10 * (size/18.0 * SYMBOL_WID) + .5) / 10.0;
    textpos = SYMBOL_WID;

    for (i=0; i<strlen(text); i++) {
	c = text[i];
	if (c < 32 || c == 0x80) c = '_'; /* not printable */
	if (c > 0xc0) c = ' '; /* F code */
        sprintf(textptr, "%g:%g:%c ", textpos, size, c);
        textptr += strlen(textptr);
        textpos += step;
    }

    bc->partial = partial;
    bc->textinfo = textinfo;
    free(codes);
    return 0;
}

/*
 * A raw 128 code is given as a sequence of space separated numbers
 * from 0 to 105, starting from the start code to be (Leonid)
 */

int Barcode_128raw_verify(unsigned char *text)
{
    int n;
    unsigned val;

    if (!strlen(text))
	return -1;
    while (*text) {
	if (sscanf(text, "%u%n", &val, &n) < 1)
	    return -1;
	if (val > 105)
	    return -1;
	text += n;
    }
    return 0;
}

int Barcode_128raw_encode(struct Barcode_Item *bc)
{
    static char *text;
    static char *partial;  /* dynamic */
    static char *textinfo; /* dynamic */
    char *textptr;
    int i, n, count, code, textpos, checksum = 0;

    if (bc->partial)
	free(bc->partial);
    if (bc->textinfo)
	free(bc->textinfo);
    bc->partial = bc->textinfo = NULL; /* safe */

    if (!bc->encoding)
	bc->encoding = strdup("128raw");

    text = bc->ascii;
    if (!text) {
        bc->error = EINVAL;
        return -1;
    }
    /*
     * length of partial code is unknown in advance, but it is
     * at most  6* (1+text/2 + check + tail) + final + terminator
     */
    partial = malloc( (3+ strlen(text)/2) * 6 + 2);
    if (!partial) {
        bc->error = errno;
        return -1;
    }

    /* the text information is at most "nnn.5:fff:c " * 1+strlen/2 +term */
    textinfo = malloc(12 * (1+strlen(text)/2) + 2);
    if (!textinfo) {
        bc->error = errno;
        free(partial);
        return -1;
    }

    strcpy(partial, "0"); /* the first space */
    textptr = textinfo;
    textpos = 0;

    for (i=0, count = 0; i < strlen(text); count++) {
	if (sscanf(text + i, "%u%n", &code, &n) < 1) {
            bc->error = EINVAL; /* impossible if text is verified */
            free(partial);
            free(textinfo);
            return -1;
	}
	strcat(partial, codeset[code]);
	
	/*
	 * since the start code is part of the "raw" input, it is
	 * counted in the checksum by itself
	 */
	if (!count) checksum += code; /* the start code */
	else        checksum += code * count; /* first * 1 + second * 2 ... */

	/*
	 * print as "%s", because we have ".5" positions
	 * also, use a size of 9 like codeC above, as each symbol is
	 * represented by two chars
	 */
        sprintf(textptr, "%g:9:%c %g:9:%c ", (double)textpos, 
		code >= 100 ? 'A' : code/10 + '0',
		textpos + (double)SYMBOL_WID/2,	code%10 + '0');
        textptr += strlen(textptr);
        textpos += SYMBOL_WID; /* width of each code */
	i += n;
    }
    /* Add the checksum, independent of BARCODE_NO_CHECKSUM */
    checksum %= 103;
    strcat(partial, codeset[checksum]);
    /* and the end marker */
    strcat(partial, codeset[STOP]);

    bc->partial = partial;
    bc->textinfo = textinfo;

    return 0;
}

