/*
 *      cairo_barcode.c
 *      
 *      Copyright 2011 Brian Starkey <stark3y[AT]gmail[DOT]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., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 *
 *      
 *      Uses the Cairo 2D graphics library to generate 8 and 13 digit
 *      UPC barcodes in PNG format.
 *  
 * 
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cairo.h>

#define L 0
#define G 1
#define R 2

typedef struct  {
	char * data;
	long int position;
} png_stream;

typedef struct {
	png_stream font_stream;
	unsigned char width;
	unsigned char height;
	unsigned char rows;
	unsigned char cols;
} font_png_stream;

extern char _binary_charset_png_start;

unsigned char UPCcode(char digit, unsigned char codeType);
cairo_surface_t * readPNGFromStream(png_stream * stream);
cairo_status_t streamReader (void *closure, unsigned char *data, unsigned int length);
cairo_surface_t *** buildCharset(font_png_stream * build_from);
void ** make2DArray(unsigned int sizeX, unsigned int sizeY, unsigned int sizeof_element);

unsigned char Lcodes[] = { 0x0D, 0x19, 0x13, 0x3D, 0x23, 0x31, 0x2F, 0x3B, 0x37, 0x0B };
unsigned char Gcodes[] = { 0x27, 0x33, 0x1B, 0x21, 0x1D, 0x39, 0x05, 0x11, 0x09, 0x17 };
unsigned char Rcodes[] = { 0x72, 0x66, 0x6C, 0x42, 0x5C, 0x4E, 0x50, 0x44, 0x48, 0x74 };
unsigned char * codes[] = {Lcodes, Gcodes, Rcodes};


unsigned char codePatterns[10][6] = {
	{L,L,L,L,L,L}, //First Digit == 0
	{L,L,G,L,G,G}, //First Digit == 1
	{L,L,G,G,L,G}, //First Digit == 2
	{L,L,G,G,G,L}, //First Digit == 3
	{L,G,L,L,G,G}, //First Digit == 4
	{L,G,G,L,L,G}, //First Digit == 5
	{L,G,G,G,L,L}, //First Digit == 6
	{L,G,L,G,L,G}, //First Digit == 7
	{L,G,L,G,G,L}, //First Digit == 8
	{L,G,G,L,G,L}  //First Digit == 9
};


int main(int argc, char * argv[]) {
	
			
	if ((argc != 2) || ((strlen(argv[1]) != 13) && (strlen(argv[1]) != 8))) {
		printf("Usage: %s [code]\n", argv[0]);
        printf("code is required (8 or 13 digits)\r\n");
        printf("Generates a PNG file with the filename '[code].png'\n");
		return 1;
	}
	else {
		unsigned char x, codePattern, len = strlen(argv[1]);
		char code[18];
		float width;
		
		strcpy(code, argv[1]);
		strcat(code, ".png");
		if (len == 13) {
			codePattern = argv[1][0] - '0';
			argv[1] = argv[1] + 1;
			len--;
			width = 115;
		}
		else {
			codePattern = 0;
			width = 87;
		}

        cairo_surface_t *surface;
        
        png_stream barcode_font_png;
		font_png_stream barcode_font;
		barcode_font_png.data = &_binary_charset_png_start;
		barcode_font.font_stream = barcode_font_png;
		barcode_font.width = 5;
		barcode_font.height = 7;
		barcode_font.rows = 1;
		barcode_font.cols = 10;
	        
        //cairo_surface_t
        cairo_surface_t **characters = *(buildCharset(&barcode_font));
        
        cairo_t *cr;
        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, 80);
        cr = cairo_create (surface);
	
		cairo_set_source_rgb (cr, 1, 1, 1);
		cairo_rectangle (cr, 0, 0, width, 80);
		cairo_fill(cr);
		cairo_stroke(cr);
		cairo_set_source_rgb (cr, 0, 0, 0);
		
		//Draw start, middle, right guard bands
		cairo_set_line_width (cr, 1);
		//start
		cairo_move_to(cr, 10.5, 0);
		cairo_rel_line_to(cr, 0, 75);
		cairo_rel_move_to(cr, 2, -75);
		cairo_rel_line_to(cr, 0, 75);
		//middle
		cairo_move_to(cr, (width/2)-1, 0);
		cairo_rel_line_to(cr, 0, 75);
		cairo_rel_move_to(cr, 2, -75);
		cairo_rel_line_to(cr, 0, 75);
		//right
		cairo_move_to(cr, width - 10.5, 0);
		cairo_rel_line_to(cr, 0, 75);
		cairo_rel_move_to(cr, -2, -75);
		cairo_rel_line_to(cr, 0, 75);
		cairo_stroke (cr);

		cairo_set_source_surface (cr, characters[codePattern], 3, 71);
		cairo_paint(cr);	
		
		for (x = 0; x < 1; x ++) {
			unsigned char i;
			for (i = 0; i < (len/2); i++) {
				unsigned char codeType = codePatterns[codePattern][i];
				unsigned char j, k, c = UPCcode(argv[1][i], codeType);
				unsigned char x_pos = (i * 7) + 14;
				unsigned char digit = argv[1][i] - '0';
				
				cairo_set_source_surface (cr, characters[digit], x_pos, 71);
				cairo_paint(cr);
				
				cairo_set_source_rgb (cr, 0, 0, 0);
				cairo_set_line_width(cr, 1);
				for (j = 64, k = 0; j > 0; j >>= 1, k++) {
					unsigned char pos = (i * 7) + k + 13;
					if (j & c) {
						cairo_move_to(cr, pos + 0.5, 0);
						cairo_rel_line_to(cr, 0, 70);
					}
				}
				cairo_stroke(cr);
			}

			for (i = (len/2); i < len; i++) {
				unsigned char j, k, c = UPCcode(argv[1][i], R);
				unsigned char x_pos = (i * 7) + 19;
				unsigned char digit = argv[1][i] - '0';
				
				cairo_set_source_surface (cr, characters[digit], x_pos, 71);
				cairo_paint(cr);
				
				cairo_set_source_rgb (cr, 0, 0, 0);
				cairo_set_line_width(cr, 1);
							
				for (j = 64, k = 0; j > 0; j >>= 1, k++) {
					unsigned char pos = (i * 7) + k + 18;
					if (j & c) {
						cairo_move_to(cr, pos + 0.5, 0);
						cairo_rel_line_to(cr, 0, 70);
					}
				}
				cairo_stroke(cr);
			}

			cairo_stroke(cr);
			
			//Write output and clean up 
			printf("Writing to '%s'...\n", code);
            cairo_surface_write_to_png (surface, code);
			cairo_destroy (cr);
			cairo_surface_destroy (surface);
		}

	}
	return 0;
}


unsigned char UPCcode(char digit, unsigned char codeType) {
	unsigned char number;
	unsigned char * codeList = codes[codeType];
	
	if ((digit >= '0') && (digit <= '9')) {
		number = digit - '0';
		return codeList[number];
	}
	return 0;
}

cairo_surface_t * readPNGFromStream(png_stream * stream) {
	stream->position = 0;
	cairo_surface_t * stream_surface = cairo_image_surface_create_from_png_stream(&streamReader, (void *)stream);
	return stream_surface;
}

cairo_status_t streamReader (void *closure, unsigned char *data, unsigned int length) {
	png_stream * stream = (png_stream *) closure;
	unsigned int i;
	for (i = 0; i < length; i++) {
		data[i] = stream->data[stream->position++];
	}
	return CAIRO_STATUS_SUCCESS;
}

cairo_surface_t *** buildCharset(font_png_stream * build_from) {
	
	cairo_surface_t * whole_set = readPNGFromStream(&(build_from->font_stream));
	cairo_surface_t *** surface_array = (cairo_surface_t ***) make2DArray(build_from->cols, build_from->rows, sizeof(cairo_surface_t*));
	
	int row, col;
	
	for (row = 0; row < build_from->rows; row++) {
		for (col = 0; col < build_from->cols; col++) {
			cairo_surface_t * char_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, build_from->width, build_from->height);
			cairo_t * char_cr = cairo_create (char_surface);
			cairo_set_source_surface (char_cr, whole_set, -col * build_from->width, -row * build_from->height);
			cairo_rectangle (char_cr, 0, 0, build_from->width, build_from->height);
			cairo_fill (char_cr);
			surface_array[row][col] = char_surface;
		}
	}
	
	return surface_array;
}


void ** make2DArray(unsigned int sizeX, unsigned int sizeY, unsigned int sizeof_element) {
	
	void ** array = (void **) malloc(sizeY * sizeof(void *));
	
	unsigned int z;
	for (z = 0; z < sizeY; z++) {
		array[z] = (void*) malloc(sizeX * sizeof_element);
	}
	return array;
}

