/**
 * The Paper Tape Project -- Visualisation sub project
 * lochstreifen.c - Painting Paper Tapes according to ECMA-10, with cairographics.
 * $Id$
 *
 * This is a rewrite from the famous Cairo paper tape drawing routines. This
 * c file implements a (simple) object called "LOCHSTREIFEN".
 *
 *
 *
 *
 *
 * (c) Copyright 2007, 2008 Sven Köppel
 *
 *  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 3 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, see
 *  http://www.gnu.org/licenses/
 *
 **/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lochstreifen.h"

/**
 * Create a new LOCHSTREIFEN object and return it. This is a quite light
 * object system implementation, as you get a reference to the object.
 * You are not supposed to manipulate the elements of the LOCHSTREIFEN
 * structure directly.
 *
 * Btw, the german word "LOCHSTREIFEN" only means "PAPER TAPE".
 *
 **/
LOCHSTREIFEN *lochstreifen_new() {
	LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));

	// set default values:
	l->data_length                = 0;
	l->data                       = NULL;
	l->highlight_region_start     = 0;
	l->highlight_region_end       = 0;
	l->highlight_region_color     = NULL;
	l->highlight_row_number       = 0;
	l->highlight_row_color        = NULL;
	l->highlight_bit_row          = 0;
	l->highlight_bit_track        = 0;
	l->highlight_bit_color        = NULL;
	l->clip                       = NULL;
	l->outer_background_color     = LOCHSTREIFEN_DEFAULT_OUTER_BACKGROUND_COLOR;
	l->papertape_background_color = LOCHSTREIFEN_DEFAULT_PAPERTAPE_BACKGROUND_COLOR;
	l->one_code_hole_color        = LOCHSTREIFEN_DEFAULT_ONE_CODE_HOLE_COLOR;
	l->zero_code_hole_color       = LOCHSTREIFEN_DEFAULT_ZERO_CODE_HOLE_COLOR;
	l->feed_hole_color            = LOCHSTREIFEN_DEFAULT_FEED_HOLE_COLOR;
	cairo_matrix_init(&l->matrix, 1, 0, 0, 1, 0, 0); // default = 1:1 original
	l->matrix_inverse             = l->matrix;
	l->row_callback               = NULL;
	l->row_callback_user_data     = NULL;

	return l;
}

/**
 * Copy everything from the LOCHSTREIFEN template, except the data. They
 * won't be copied, just set, because LOCHSTREIFEN doesn't manage your
 * data. If you want to copy them, call something like
 *   LOCHSTREIFEN* copy = lochstreifen_copy(old);
 *   byte_t* new_data = malloc(old->data_length);
 *   memcpy(new_data, old->data, old->data_length);
 *   lochstreifen_set_data(copy, old->data_length, new_data);
 **/
LOCHSTREIFEN* lochstreifen_copy(const LOCHSTREIFEN* template) {
	LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));
	#define COPY_RGBA_PATTERN(name) { double r,g,b,a; \
		if(!cairo_pattern_get_rgba(template->name, &r, &g, &b, &a)) \
			l->name = cairo_pattern_create_rgba(r,g,b,a); }
	
	// make it quite verbose ;-)
	l->data_length                = template->data_length;
	l->data                       = template->data;
	l->highlight_region_start     = template->highlight_region_start;
	l->highlight_region_end       = template->highlight_region_end;
	COPY_RGBA_PATTERN(highlight_region_color);
	l->highlight_row_number       = template->highlight_row_number;
	COPY_RGBA_PATTERN(highlight_row_color);
	l->highlight_bit_row          = template->highlight_bit_row;
	l->highlight_bit_track        = template->highlight_bit_track;
	COPY_RGBA_PATTERN(highlight_bit_color);
	l->clip                       = malloc(sizeof(cairo_rectangle_t));
	memcpy(l->clip, template->clip, sizeof(cairo_rectangle_t));
	COPY_RGBA_PATTERN(outer_background_color);
	COPY_RGBA_PATTERN(papertape_background_color);
	COPY_RGBA_PATTERN(one_code_hole_color);
	COPY_RGBA_PATTERN(zero_code_hole_color);
	COPY_RGBA_PATTERN(feed_hole_color);
	l->matrix                     = template->matrix;
	l->matrix_inverse             = template->matrix_inverse;
	l->row_callback               = template->row_callback;
	l->row_callback_user_data     = template->row_callback_user_data;

	return l;
}

/**
 * Delete every mallocated structure in the LOCHSTREIFEN (colors, etc.),
 * *except* from the data. You have to manage them on your own.
 * In the end, it will free the LOCHSTREIFEN itself.
 * This won't touch your row_callback_user_data.
 * @return Nothing, because there's nothing left. Except your data, okay.
 **/
void lochstreifen_free(LOCHSTREIFEN* l) {
	if(l->highlight_region_color     != NULL)  free(l->highlight_region_color);
	if(l->highlight_row_color        != NULL)  free(l->highlight_row_color);
	if(l->highlight_bit_color        != NULL)  free(l->highlight_bit_color);
	if(l->clip                       != NULL)  free(l->clip);
	if(l->outer_background_color     != NULL)  free(l->outer_background_color);
	if(l->papertape_background_color != NULL)  free(l->papertape_background_color);
	if(l->one_code_hole_color        != NULL)  free(l->one_code_hole_color);
	if(l->zero_code_hole_color       != NULL)  free(l->zero_code_hole_color);
	if(l->feed_hole_color            != NULL)  free(l->feed_hole_color);
}

/**
 * A small debug function that will print out all fields of the LOCHSTREIFEN
 * structure well ordered to stderr.
 *
 **/
void lochstreifen_print_debug(LOCHSTREIFEN* l) {
	int x;
	#define COLORP(color) if(color == NULL) fprintf(stderr, "   color ist NULL.\n"); \
		else if(cairo_pattern_get_type(color) == CAIRO_PATTERN_TYPE_SOLID) {\
		double r,g,b,a; cairo_pattern_get_rgba(color, &r, &g, &b, &a); \
		fprintf(stderr, "   SOLID: R=%f, G=%f, B=%f, A=%f\n", r, g, b, a); \
		} else fprintf(stderr, "    NO SOLID PATTERN (special pattern)\n");
	#define COLORP_VERBOSE(color1, name) \
		fprintf(stderr, " * "name": %s\n", (color1) != NULL ? "ENABLED" : "disabled");\
		COLORP(color1);

	
	if(l != NULL)
		fprintf(stderr, "Debugging papertape at %p:\n", l);
	else {
		fprintf(stderr, "Cannot debug: Papertape is NULL!\n");
		return;
	}
	
	// 1. Point: Print out data.
	fprintf(stderr, "1# DATA AND OUTPUT IMAGE SIZE INFORMATION\n");
	fprintf(stderr, " * Data: length=%i bytes", l->data_length);
	if(l->data_length > 0 && l->data != NULL) {
		// there are data
		fprintf(stderr, ":\n   { ");
		for(x=0;;) {
			fprintf(stderr, "0x%x", l->data[x]);
			if(++x == l->data_length) {
				fprintf(stderr, " };\n");
				break;
			} else if(x == 10) { // magic number: print first 10 bytes.
				fprintf(stderr, ", ... };\n");
				break;
			} else
				fprintf(stderr, ", ");
		}
	} else if(l->data == NULL) {
		fprintf(stderr, " BUT data are NULL!\n");
	} else  // data_length == 0.
		fprintf(stderr, " (empty data)\n");
	fprintf(stderr, " * Unscaled dimensions:\n");
	fprintf(stderr, "   width of image (length of papertape) = %f\n", lochstreifen_get_length(l));
	fprintf(stderr, "   height of image (width of papertape) = 1\n");
	
	// 2. Point: Print out colors
	fprintf(stderr, "\n2# ELEMENT COLORS\n");
	COLORP_VERBOSE(l->outer_background_color, "outer background");
	COLORP_VERBOSE(l->papertape_background_color, "papertape background");
	COLORP_VERBOSE(l->one_code_hole_color, "one code holes");
	COLORP_VERBOSE(l->zero_code_hole_color, "zero code holes");
	COLORP_VERBOSE(l->feed_hole_color, "feed holes");
	
	// 3. Point: Print out highlighted thingies
	fprintf(stderr, "\n3# HIGHLIGHT COLORS\n");
	fprintf(stderr, l->highlight_region_color != NULL ? " * Highlight region: start=%i end=%i\n"
	        : " * Highlight region: disabled\n", l->highlight_region_start, l->highlight_region_end);
	COLORP(l->highlight_region_color);
	fprintf(stderr, l->highlight_row_color != NULL ? " * Highlight row: number=%i\n"
		: " * Highlight row: disabled\n", l->highlight_row_number);
	COLORP(l->highlight_row_color);
	fprintf(stderr, l->highlight_bit_color != NULL ? " * Higlight bit: row=%i track=%i\n"
		: " * Highlight bit: disabled\n", l->highlight_bit_row, l->highlight_bit_track);
	COLORP(l->highlight_bit_color);
	
	// 4. Point: Print out information about clippings
	fprintf(stderr, "\n4# SPECIAL STRUCTURES\n");
	if(l->clip == NULL)
		fprintf(stderr, " * Clipping: disabled\n");
	else {
		fprintf(stderr, " * Clipping: ENABLED\n");
		fprintf(stderr, "  x=%f, y=%f, width=%f, height=%f\n",
			l->clip->x, l->clip->y, l->clip->width, l->clip->height);
	}
	
	// 5. Point: Affine Matrix
	fprintf(stderr, " * Current transformation matrix:\n");
	fprintf(stderr, "   x_new = [xx=%f]*x + [xy=%f]*y + [x0=%f]\n",
		l->matrix.xx, l->matrix.xy, l->matrix.x0);
	fprintf(stderr, "   y_new = [yx=%f]*x + [yy=%f]*y + [y0=%f]\n",
		l->matrix.yx, l->matrix.yy, l->matrix.y0);
	fprintf(stderr, "   source dimensions: x_max = %f x y_max = %f\n",
		LOCHSTREIFEN_LENGTH, LOCHSTREIFEN_WIDTH);
	fprintf(stderr, "   target dimensions: width = %i x height = %i\n",
		lochstreifen_get_target_width(l),
		lochstreifen_get_target_height(l));
	
	// 6. Point: Debugging
	fprintf(stderr, " * Debugging: %s\n", l->debug ? "ENABLED" : "disabled");
	
	fprintf(stderr, "END OF LOCHSTREIFEN\n");
}

/**
 * Set the data which LOCHSTREIFEN is supposed to punch out. You shall give
 * the LOCSTREIFEN object only a copy of your data, never give it a sensible
 * data array. The LOCHSTREIFEN object won't write into your data, but afterwards
 * it will free them. So it treats the data as it's own.
 *
 **/
void lochstreifen_set_data(LOCHSTREIFEN *l, int data_length, byte_t *data) {
	l->data_length = data_length;
	//if(l->data != NULL)
	//	free(l->data);
	l->data = data;
}

/**
 * Adds Null Bytes (0x00) at start and/or end of the data array. This dynamically
 * increases the data array.
 *
 *
 **/
void lochstreifen_add_null_bytes(LOCHSTREIFEN *l, int start, int end) {
	byte_t *new_data;

	// only to be sure realloc won't decrease the size of the array:
	if(start < 0) start = 0;
	if(end < 0)   end = 0;
	
	// make array bigger to contain new start/end chunks
	new_data = realloc(l->data, l->data_length + start + end);
	// check wheter realloc exited successfully. We don't want segfaults
	if(new_data == NULL)
		return;
	// move data to the middle
	memmove(new_data + start, new_data, l->data_length);
	// fill new start chunk with zero bytes
	memset(new_data, 0x00, start);
	// fill new end chunk with zero bytes
	memset(new_data + start + l->data_length, 0x00, end);
	// that's it.
	
	// fix the data link. Don't free the old data. Don't know exactly
	// how realloc works, sorry ;-)
	l->data = new_data;
}

/**
 * Get the length of a LOCHSTREIFEN. It depends on the length of the data array
 * and the papertape dimension constants.
 *
 *
 **/
double lochstreifen_get_length(LOCHSTREIFEN *l) {
	return LOCHSTREIFEN_LENGTH;
}


/**
 * Tell LOCHSTREIFEN to highlight a region of rows. Start counts from 0 and is already
 * in the selection, end is no more in selection. Example with start = 2; end = 5:
 *
 * 0    1     2     3     4     5    6  <- rows
 *         |_________________|
 *          highlight region
 **/
void lochstreifen_set_highlight_region(LOCHSTREIFEN *l, row_t start, row_t end) {
	l->highlight_region_start = start;
	l->highlight_region_end   = end;
	if(l->highlight_region_color == NULL)
		l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_REGION_COLOR;
}

/**
 * Get the selection bounds of the highlighted rows. Give Links to your variables
 * where the function stores the start/end integers.
 * @return TRUE if there is an highlight selection, FALSE otherwiese
 **/
int lochstreifen_get_highlight_region(LOCHSTREIFEN *l, row_t *start, row_t *end) {
	if(l->highlight_region_color == NULL || l->highlight_region_start == LOCHSTREIFEN_NO_ROW)
		return 0;
	if(start != NULL)
		*start = l->highlight_region_start;
	if(end != NULL)
		*end   = l->highlight_region_end;
	return 1;
}

/**
 * Remove the highlighted region (that is, make it no more highlighted).
 **/
void lochstreifen_remove_highlight_region(LOCHSTREIFEN *l) {
	//l->highlight_region_color = NULL;
	// was stupid, this is more intelligent:
	l->highlight_region_start = LOCHSTREIFEN_NO_ROW;
}

/**
 * Tell LOCHSTREIFEN to highlight one, only one special row. Starts counting from 0.
 * Example with start = 3:
 *
 * 0   1    2     3     4     5
 *             |_____|
 *        highlighted row
 *
 * Principally this is the same feature like lochstreifen_set_highlight_region, having
 * (in the above example) start = 3 and end = 4. You can just use another color and
 * use both techniques concurrently, whereby the highlighted row will ALWAYS be ABOVE
 * the highlighted chunk of rows. An application example is e.g.: The highlighted region
 * of rows are selected rows, the highlighted special row is the cursor position row.
 * This would be quite usable with an implementation of the GtkEditable interface, like
 * GtkPaperTape.
 **/
void lochstreifen_set_highlight_row(LOCHSTREIFEN *l, row_t number_of_row) {
	l->highlight_row_number = number_of_row;
	if(l->highlight_row_color == NULL)
		l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_ROW_COLOR;
}

/**
 * Get the highlighted special row. If there is no highlighted row, it will return
 * LOCHSTREIFEN_NO_ROW.
 * @return the number of the special row, counting from zero. 
 **/
int lochstreifen_get_highlight_row(LOCHSTREIFEN *l) {
	return l->highlight_row_number;
}

/**
 * Remove the highlight at the special selected row.
 **/
void lochstreifen_remove_highlight_row(LOCHSTREIFEN *l) {
	//l->highlight_row_color = NULL;  /// nein, das ist dumm, weil es den highlight komplett entfernen würde.
	l->highlight_row_number = LOCHSTREIFEN_NO_ROW; // das ist schlau.
}

/**
 * Tell LOCHSTREIFEN to highlight one special bit on the complete papertape. To identify
 * this bit, you give the row and track number (byte id and bit id) for this bit. E.g.
 * with row = 3, bit = 5 you'll get something like:
 *
 * ------- DIRECTION OF MOVEMENT ------->
 * track 8     8     8     8     8     8
 * track 7     7     7     7     7     7
 * track 6     6     6  +-----+  6     6
 * track 5     5     5  |  5  |  5     5       (of course the highlight won't affect any
 * track 4     4     4  +-----+  4     4        other bit, row 3, track 6 and track 4
 * feed  -     -     -     -     -     -        are not visible due to ASCII limitations
 * track 3     3     3     3     3     3        ;-) )
 * track 2     2     2     2     2     2
 * track 1     1     1     1     1     1
 * 
 * row:  0     1     2     3     4     5
 *                         ^-- this row
 *
 * The bit highlight will appear on top of the row highlight and region highlight. You
 * can combine the bit highlight with row highlight (e.g. in the example above you can
 * highlight row 3, too) and even with the region highlight (e.g. having a region from
 * row 2 to row 5).
 **/
void lochstreifen_set_highlight_bit(LOCHSTREIFEN *l, row_t number_of_row, track_t number_of_bit) {
	l->highlight_bit_row = number_of_row;
	l->highlight_bit_track = number_of_bit;
	if(l->highlight_bit_color == NULL)
		l->highlight_bit_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_BIT_COLOR;
}

/**
 * Get the highlighted special bit. This works exactly like lochstreifen_get_highlight_region,
 * returning FALSE (0) if there's no bit selected.
 *
 **/
int lochstreifen_get_highlight_bit(LOCHSTREIFEN *l, row_t *number_of_row, track_t *number_of_bit) {
	if(l->highlight_bit_row == LOCHSTREIFEN_NO_ROW)
		return 0;
	if(number_of_row != NULL)
		*number_of_row = l->highlight_bit_row;
	if(number_of_bit != NULL)
		*number_of_bit = l->highlight_bit_track;
	return 1;
}

/**
 * Remove the highlighting of the special bit.
 **/
void lochstreifen_remove_highlight_bit(LOCHSTREIFEN *l) {
	//l->highlight_bit_color = NULL; // stupid.
	l->highlight_bit_row = LOCHSTREIFEN_NO_ROW; // more intelligent.
}

/**
 * Whenever you change the matrix, call this function. It will just perform some
 * cleanup work, espacially calculate the inverse matrix for the other-side calculations.
 **/
void lochstreifen_check_matrix(LOCHSTREIFEN* l) {
	l->matrix_inverse = l->matrix; // copy matrix
	if(cairo_matrix_invert(&l->matrix_inverse) != CAIRO_STATUS_SUCCESS) { // invert the copy
		// there are some matrizes which are not invertable
		fprintf(stderr, "lochstreifen_check_matrix: Matrix is not invertable!\n");
	}
}

/**
 * If you want LOCHSTREIFEN only to paint a small area from the whole papertape, this
 * is the function you can call.
 * If you are using an affine transformation matrix for scaling/rotation/... purpose,
 * this function will automatically transform these values. So don't give here values
 * like "LOCHSTREIFEN_WIDTH" or "LOCHSTREIFEN_HEIGHT" ;-)
 *
 **/
void lochstreifen_set_clip(LOCHSTREIFEN *l, double x, double y, double width, double height) {
	if(l->clip == NULL)
		l->clip = malloc(sizeof(cairo_rectangle_t));

	cairo_matrix_transform_point(&l->matrix_inverse, &x, &y);
	cairo_matrix_transform_distance(&l->matrix_inverse, &width, &height);
	l->clip->x = x;
	l->clip->y = y;
	l->clip->width = width;
	l->clip->height = height;
}

/**
 * I fyou want LOCHSTREIFEN no more to paint only a small area but the whole paper tape,
 * simply call this function.
 **/
void lochstreifen_remove_clip(LOCHSTREIFEN *l) {
        free(l->clip);
        l->clip = NULL; // not neccessary, I suppose.
}

/**
 * Affect the current transformation matrix so the papertape finally has a length of
 * exactly the value given in the argument, in pixels. The length depends from the
 * number of rows on the papertape, so the length will increase if you increase the
 * number of rows after calling lochstreifen_set_scaling_by_length.
 **/
void lochstreifen_set_scaling_by_length(LOCHSTREIFEN *l, int length) {
	double scale_factor = ((double)length) / LOCHSTREIFEN_LENGTH;
	printf("Scaling to length=%i, having length=%f, getting %f as scale factor\n",
		length, LOCHSTREIFEN_LENGTH, scale_factor);
	cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
	lochstreifen_check_matrix(l);
}

void lochstreifen_set_scaling_by_width(LOCHSTREIFEN *l, int width) {
	double scale_factor = ((double)width) / LOCHSTREIFEN_WIDTH;
	cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
	lochstreifen_check_matrix(l);
}

void lochstreifen_set_scaling_by_code_hole(LOCHSTREIFEN *l, int diameter) {
	double scale_factor = ((double)diameter)/2 / LOCHSTREIFEN_RADIUS_CODE_HOLE;
	cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
	lochstreifen_check_matrix(l);
}

/**
 * Get the dimensions of the paper tape in the output image. Be careful: While
 * width/length are well defined in the LOCHSTREIFEN process, hereby width and
 * height mean the dimensions of the rectangular surface picture.
 * If you need only the value of one dimension, use
 * lochstreifen_get_target_width or lochstreifen_get_target_height, respectively.
 *
 * The calculation of these values is quite expensive, so you do well when
 * caching the results.
 * @return nothing, because Call-by-Reference.
 **/
void lochstreifen_get_target_dimensions(LOCHSTREIFEN *l, int *width, int *height) {
	int i;
	// preset the dimensions to 0
	if(width  != NULL)  *width = 0;
	if(height != NULL)  *height = 0;
	// define the "bounding box" where the papertape resides in
	// user space cordinates (like lochstreifen_draw paints)
	double x[] = { 0, LOCHSTREIFEN_LENGTH,                  0, LOCHSTREIFEN_LENGTH };
	double y[] = { 0,                   0, LOCHSTREIFEN_WIDTH, LOCHSTREIFEN_WIDTH  };
	// transform the "bounding box" throught the matrix
	for(i=0; i < (sizeof(x) / sizeof(double)); i++) {
		cairo_matrix_transform_point(&l->matrix, &x[i], &y[i]);
		// store biggest x/y values
		if(width != NULL && x[i] > *width)
			*width = x[i];
		if(height != NULL && y[i] > *height)
			*height = y[i];
	}
}

/**
 * Get width of the output picture. This is only a nicer frontend to
 * lochstreifen_get_target_dimensions that returns the width value.
 **/
int lochstreifen_get_target_width(LOCHSTREIFEN *l) {
	int width;
	lochstreifen_get_target_dimensions(l, &width, NULL);
	return width;
}

/**
 * Get height of the output picture. This is only a nicer frontend to
 * lochstreifen_get_target_dimensions that returns the height value.
 **/
int lochstreifen_get_target_height(LOCHSTREIFEN *l) {
	int height;
	lochstreifen_get_target_dimensions(l, NULL, &height);
	return height;
}

/**
 * Get a bit tuple from the positions on the target surface. This function
 * translates the points x/y with the inverse current translation matrix. Then
 * it calculates the row number that equals with the x coordinate and afterwards
 * the track number that equals with the y coordinate.
 *
 * If you need only one dimension, set the other NULL.
 *
 * You can use lochstreifen_get_traget_row_from_point and
 * lochstreifen_get_target_track_from_point if you need only one dimension. Furthermore
 * they will nicely return the requested data -- no need for call by reference.
 *
 * @param bit The target lochstreifen_bit_t (call by reference). Should not be NULL.
 * @param x  The x coordinate you want to check
 * @param y  The y coordinate you want to check
 **/
void lochstreifen_get_target_bit_from_point(LOCHSTREIFEN* l, row_t* row, track_t* track, double x, double y) {
	cairo_matrix_transform_point(&l->matrix_inverse, &x, &y); // transform points
	
	if(y < 0 || y > LOCHSTREIFEN_WIDTH) {
		// we are not at data, at all
		if(row != NULL)
			*row = LOCHSTREIFEN_NO_ROW;
		if(track != NULL)
			*track = LOCHSTREIFEN_NO_TRACK;
		return;
	}
	
	if(row == NULL) {
		// if only the track was requested, create a temporary row buffer, because
		// we need the row number as a basis for the correct track calculation.
		row_t row_buffer;
		row = &row_buffer;
	}
	
	// Row calculation is fairly easy:
	if(x < LOCHSTREIFEN_TEAR_OFF_LENGTH || x > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH)) {
		// we are not at data
		*row = LOCHSTREIFEN_NO_ROW;
	} else {
		// getting the row from the x value. This is pretty much the same algorithm
		// like used in papertape_draw for checking up the rectangle bounding box (clipping).
		*row = floor(
			(x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
		);
	}
	
	// Track calculation is much more complex:
	if(track != NULL) {
		/* get the track from the y value. This is not simply something like
		 *   *track = floor( y / LOCHSTREIFEN_TRACK_DISTANCE );
		 * because we must take care that a track is a round thing with a
		 * specific height.
		 */
		double M_x, M_y; // the centers of the circles
		int i; // some iterator
		// The center of this circle, fairly complex to calculate, see lochstreifen_draw.
		M_x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (*row + 0.5) * LOCHSTREIFEN_ROW_DISTANCE;
		M_y = LOCHSTREIFEN_TRACK_DISTANCE; // the first track, to get onto papertape
		// this a genious canidate for loop unrolling:
		for(i=0; i < 8; i++) {
			// simple linear algebra: Check if point is in the circle.
			//fprintf(stderr, "bit %d: (%f|%f) <-> P(%f|%f) <= %f\n", i, M_x, M_y, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE);
			if(hypot( (x-M_x) , (y-M_y) ) <= LOCHSTREIFEN_RADIUS_CODE_HOLE) {
				// the point is in this circle, so we have found the correct track.
				//fprintf(stderr, "FOUND %d\n", i);
				*track = i;
				return;
			} else {
				// go on, set M for next circle
				M_y += LOCHSTREIFEN_TRACK_DISTANCE;
				// jump over feed track before i == 3.
				if(i == 2)
					M_y += LOCHSTREIFEN_TRACK_DISTANCE;
			}
		} // for tracks
		
		// cursor not over circle:
		*track = LOCHSTREIFEN_NO_TRACK;
		//fprintf(stderr, "NOT FOUND TRACK.\n");
	} // y dimension
} // lochstreifen_get_target_bit_on_point

/**
 * Get the row number from an x/y target coordinate. That is: At first, translate
 * the points with the current translation matrix, afterwards calculate the row
 * number for the position.
 *
 * @returns Row number, counting from zero. Negative value if point not on papertape data chunk.
 **/
row_t lochstreifen_get_target_row_from_point(LOCHSTREIFEN* l, double x, double y) {
	row_t row;
	lochstreifen_get_target_bit_from_point(l, &row, NULL, x, y);
	return row;
}

track_t lochstreifen_get_target_track_from_point(LOCHSTREIFEN* l, double x, double y) {
	track_t track;
	lochstreifen_get_target_bit_from_point(l, NULL, &track, x, y);
	return track;
}


/**
 * Calculate the rectangle that contains the complete row on the LOCHSTREIFEN,
 * in target surface dimensions (using the transformation matrix). If the operation
 * fails (if the row doesn't exist), the rect is not touched at all.
 *
 * @param row Number of Row you want
 * @param rect Call by reference like rectangle type
 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW.
 **/
int lochstreifen_get_target_rect_from_row(LOCHSTREIFEN* l, row_t row, cairo_rectangle_t* rect) {
	// check wheter we are in bounds
	if(row < 0 || row > l->data_length)
		return LOCHSTREIFEN_NO_ROW;

	// This rectangle is a bit bigger than the real track, so that outlines, etc.
	// could be drawn there, too.
	
	rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
	rect->y = 0;
	rect->width = LOCHSTREIFEN_ROW_DISTANCE;
	rect->height = LOCHSTREIFEN_WIDTH;
	
	// take this as padding:
	#define TARGET_RECT_HOW_MUCH_BIGGER  (LOCHSTREIFEN_ROW_DISTANCE * 0.1)
	rect->x -= TARGET_RECT_HOW_MUCH_BIGGER;
	rect->y -= TARGET_RECT_HOW_MUCH_BIGGER;
	rect->width += 2*TARGET_RECT_HOW_MUCH_BIGGER;
	rect->height += 2*TARGET_RECT_HOW_MUCH_BIGGER;
	
	// transform the points
	cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
	cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
	
	return LOCHSTREIFEN_SUCCESS;
}

/**
 * This is just the same like lochstreifen_get_target_rect_from_row, just with
 * bits. This is still a rectangle which contains the complete circle that represents
 * the bit, useful e.g. for GDK expose events.
 *
 * @param row Number of Row
 * @param track Number of Track
 * @param rect Call by reference like rectangle type
 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW or LOCHSTREIFEN_NO_TRACK
 **/
int lochstreifen_get_target_rect_from_bit(LOCHSTREIFEN* l, row_t row, track_t track, cairo_rectangle_t* rect) {
	// check bounds
	if(row < 0 || row > l->data_length)
		return LOCHSTREIFEN_NO_ROW;
	if(track < 0 || track > 7)
		return LOCHSTREIFEN_NO_TRACK;

	// the rectangle is doesn't touch the circle, but leaves quite a bit space around
	// it, so outlines, etc. should not be a problem.

	// this code is quite like the beginning x/y dimensions in lochstreifen_draw():
	rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
		  //+ (LOCHSTREIFEN_ROW_DISTANCE - LOCHSTREIFEN_RADIUS_CODE_HOLE*2) / 2; // the center
	rect->y = LOCHSTREIFEN_TRACK_DISTANCE * (track + (track > 2 ? 1 : 0) );
	// The correct values for width/height would be:
	//    rect->width = LOCHSTREIFEN_RADIUS_CODE_HOLE * 2;
	// but that won't at exposures if the hole is painted e.g. with an outline (cairo_stroke).
	// We therefore simply take the complete track as width
	rect->width = LOCHSTREIFEN_ROW_DISTANCE;
	rect->height = LOCHSTREIFEN_TRACK_DISTANCE * 2;
	
	// transform to user space
	cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
	cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
	
	return LOCHSTREIFEN_SUCCESS;
}

/**
 *
 * Just to be sure that there are no misunderstandings: Rotation (wiht cairo)
 * principally works like this:
 *
 *              |  B----D
 *                 | V  |
 *    180°      |  | V  |
 * D--------C      | V  | 270°
 * |>>>>>>>>|   |  | V  |
 * B--------A      A----C
 *              |
 *   - - - - - -+------------------------>   x axis
 *              |                    .
 *      C----A  |  A------------B    .
 *      | ^  |  |  |<<<<<<<<<<<<|    .
 *  90° | ^  |  |  C------------D    .
 *      | ^  |  |     original       .  <- borders of the target surface
 *      | ^  |  |....................+
 *      D----B  |
 *              V
 *           y axis
 *
 * Unfortunately every rotated box would be outside of the target surface
 * (e.g. an 200x100px PNG image), thus we must move the matrix inside the
 * positive x/y axis intercept.
 * This can be performed quite simple
 *
 **/
void lochstreifen_set_rotation(LOCHSTREIFEN *l, enum lochstreifen_direction direction) {
	switch(direction) {
		case LOCHSTREIFEN_MOVEMENT_TO_LEFT:
			// standard
		case LOCHSTREIFEN_MOVEMENT_TO_TOP:
			//cairo_matrix_rotate();
		case LOCHSTREIFEN_MOVEMENT_TO_RIGHT:
			
		case LOCHSTREIFEN_MOVEMENT_TO_BOTTOM:
		
		case LOCHSTREIFEN_MOVEMENT_NONE:
		default:
			break;
	}
	lochstreifen_check_matrix(l);
}


/**
 * The very central main function for the LOCHSTREIFEN object: Drawing.
 * You are supposed to create an adequate cairo context.
 *
 * This basic drawing function will draw relative to the width of the
 * paper tape, like defined as 1 inch in the ECMA-10 standard. This
 * width will have the value "1".
 *
 * Your cropping/... blub shall treat hereby. blabla.
 **/
void lochstreifen_draw(LOCHSTREIFEN *l, cairo_t *cr) {
	// complete length of paper tape:
	double length = LOCHSTREIFEN_LENGTH;

	// iterators for the byte/bit loops
	int row,track;
	// position while iterating
	double x,y;
	// max positions to iterate to
	int row_max, track_min, track_max;

	// apply the matrix!
	cairo_set_matrix(cr, &l->matrix);
	
	// check if we need to repaint the papertape
	if(l->clip != NULL && (l->clip->y > LOCHSTREIFEN_WIDTH || l->clip->x > LOCHSTREIFEN_LENGTH) ) {
		// Clipping region is outside of papertape!
		//printf("draw: Outside papertape.\n");
		return;
	}

	// paint outer background color.
	if(l->outer_background_color != NULL) {
		cairo_set_source(cr, l->outer_background_color);
		cairo_paint(cr);
	}

	// paint the paper tape outline, including the tear-off
	if(l->papertape_background_color != NULL) {
		cairo_set_source(cr, l->papertape_background_color);

		// tear-off at the beginning (0.5 of paper tape width)
		cairo_move_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.8, 0);
		cairo_line_to(cr, 0, 4*LOCHSTREIFEN_TRACK_DISTANCE);
		cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
		cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
		cairo_close_path(cr);
		cairo_fill(cr);
		
		// long rectangular area over complete paper tape
		if(l->clip == NULL) {
			cairo_rectangle(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0.0,
				length - 2*LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
			// irc.gnome.org, #gtk+:
			// <owen> sven__: a basic comment is that if you draw a *single* shape with cairo
			// that's bigger than 32k pixels in any dimension, it's unlikely to work
			// [17:51] <owen> sven__: for the Xlib backend. this is a lot more fixable now 
			// that Cairo switched to 24.8 fixed point internally, but I don't think anybody 
			// has done the work to fix it
		} else {
			// calculate the neccessary width
			// if clipping region exceeds lochstreifen width.... make it shorter, else let it.
			double width = ( (l->clip->x + l->clip->width) > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
					? ( LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH - l->clip->x )
					: l->clip->width;
			// don't have the clipping region paint in the tear off
			// right, so crop it at the left.
			if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH)
				width -= LOCHSTREIFEN_TEAR_OFF_LENGTH;
			cairo_rectangle(cr,
				(l->clip->x >= LOCHSTREIFEN_TEAR_OFF_LENGTH) ? 
					l->clip->x : LOCHSTREIFEN_TEAR_OFF_LENGTH,
				0,
				width,
				LOCHSTREIFEN_WIDTH);
		}
		cairo_fill(cr);
		

		// tear-off at the end (0.5 of paper tape width)
		cairo_move_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
		cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
		cairo_line_to(cr, length, LOCHSTREIFEN_WIDTH);
		cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 4*LOCHSTREIFEN_TRACK_DISTANCE);
		cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.2, 0);
		cairo_close_path(cr);
		cairo_fill(cr);
		
	} // paper tape background finished.
	
	// paint feed holes in the tear-off at the beginning
	// only if in clipped area
	if( ( l->clip == NULL || (l->clip->x <= LOCHSTREIFEN_TEAR_OFF_LENGTH) )
	 && (l->feed_hole_color != NULL) ) {
		cairo_set_source(cr, l->feed_hole_color);
		for(y=4*LOCHSTREIFEN_TRACK_DISTANCE,
		    x=LOCHSTREIFEN_TEAR_OFF_LENGTH - LOCHSTREIFEN_ROW_DISTANCE/2;
		    x > 0; x -= LOCHSTREIFEN_ROW_DISTANCE)
			cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
		cairo_fill(cr);
	}

	
	if(l->clip != NULL) {
		/* calculate clipping in dimension X (rows, bytes).
		 * Clipping in this dimension is very important, especially at very long
		 * papertapes. Application software (like GtkPaperTape) usually zooms the
		 * papertape, so it won't fit completely on the screen. So skipping bytes
		 * which are not displayed is very performant.
		 */
		if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH) {
			// begin before tear off
			row = 0;
			row_max = floor(
				( l->clip->width - (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
				/ LOCHSTREIFEN_ROW_DISTANCE
			);
		} else {
			// after tear off
			row = floor (
				(l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
			);
			
			// end with just one more row as neccessary
			row_max = row + 1 + ceil(
				( l->clip->width ) / LOCHSTREIFEN_ROW_DISTANCE
			);
		}
		
		// don't go over the data range
		if(row_max > l->data_length)
			row_max = l->data_length;
		// dont' have row_max smaller than row
		//if(row_max < row)
		//	row_max = row;

		/* calculate clipping in dimension Y (tracks, bits)
		 * Y clipping is not as important, because there are only 9 circles
		 * to paint. The feed hole (which is painted just in the loop for the
		 * 3. track hole is not counted, therefore the calculations become somewhat
		 * inneccessary complex, compared to the painting overhead for the complete
		 * 8 tracks instead of e.g. only 6 tracks. Apart from that, application software
		 * usually doesn't clip the papertape this way, because users usually want to
		 * see a complete byte and not only a half byte.
		 */
		// calculation of first track to begin with.
		/*
		track_min = l->clip->y == 0 ? 0 : floor( l->clip->y / LOCHSTREIFEN_TRACK_DISTANCE );
		
		
		// calculation of last track to end with.
		if(l->clip->y + l->clip->height > LOCHSTREIFEN_WIDTH)
			track_max = 8; // clipping region is *much* bigger than the papertape width
		else {
			// just paint one more than neccessary
			track_max = 1 + 8 - floor(
				(LOCHSTREIFEN_WIDTH - (l->clip->y + l->clip->height))
				/ LOCHSTREIFEN_TRACK_DISTANCE
			);
			// dont't go over the track range
			if(track_max > 8)
				track_max = 8;
		}*/
		track_min = 0;
		track_max = 8;
	} else {
		// no clipping. Paint *everything*.
		row = 0;
		row_max = l->data_length;
		
		track_min = 0;
		track_max = 8;
	}
	
	if(l->clip != NULL) {
		double x,y,width,height;
		x=l->clip->x; y=l->clip->y; width=l->clip->width; height=l->clip->height;
		cairo_user_to_device(cr, &x, &y);
		cairo_user_to_device_distance(cr, &width, &height);
		printf("draw: %i|%i %ix%i ", (int)x, (int)y, (int)width, (int)height);
	} else    printf("draw: ");
	printf("row %i->%i track %i->%i\n", row, row_max, track_min, track_max);
	
	// loop all the rows (bytes)
	// x start: tear off + one half row offset + offset rows * length of row
	for(x=LOCHSTREIFEN_TEAR_OFF_LENGTH + LOCHSTREIFEN_ROW_DISTANCE/2 + row*LOCHSTREIFEN_ROW_DISTANCE;
	    row < row_max; row++, x += LOCHSTREIFEN_ROW_DISTANCE) {

		// call row callback
		if(l->row_callback != NULL) {
			printf("CALLING ROW CALLBACK!\n");
			(*l->row_callback) (&row, cr, l->row_callback_user_data);
			// at least now that's all the magic ;)
		}
		
		// highlight region?
		if(l->highlight_region_color != NULL && l->highlight_region_start != LOCHSTREIFEN_NO_ROW &&
		    (row >= l->highlight_region_start || row < l->highlight_region_end) ) {
			cairo_set_source(cr, l->highlight_region_color);
			// hier was schlaues schickeres als ein RECHTECK
			// einfallen lassen. Vielleicht mit abgerundetem
			// Rechteck? Wuerde zu den Löchern passen.
			// http://www.cairographics.org/cookbook/roundedrectangles/
			cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE / 2, 0,
			   LOCHSTREIFEN_TRACK_DISTANCE * (l->highlight_region_end - l->highlight_region_start),
			   LOCHSTREIFEN_WIDTH);
			cairo_fill(cr);
		}
		
		// highlight (only) this row?
		if(l->highlight_row_color != NULL && row == l->highlight_row_number) {
			// if check again LOCHSTREIFEN_NO_ROW not neccessary because row iterator never
			// gets LOCHSTREIFEN_NO_ROW.
			cairo_set_source(cr, l->highlight_row_color);
			// genauso abgerundetes Rechteck wie bei der region
			// überlegen.
			cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE/2, 0,
			    LOCHSTREIFEN_TRACK_DISTANCE, LOCHSTREIFEN_WIDTH);
			cairo_fill(cr);
		}
		
		// loop all the tracks (bits)
		// y start: offset tracks * width of track + one track (to get onto the papertape)
		for(track=track_min, y = LOCHSTREIFEN_TRACK_DISTANCE*(track+1);
		    track < track_max; track++, y += LOCHSTREIFEN_TRACK_DISTANCE) {
			if(track == 3) {
				// the feed hole is at position 3
				if(l->feed_hole_color != NULL) {
					// we paint a feed hole :-)
					cairo_set_source(cr, l->feed_hole_color);
					cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
					cairo_fill(cr);
				}
				// jump one track, in every case.
				y += 0.1;
			}
			
			printf("testing byte\n");
			if( ((l->data[row] >> track) & 0x01) != 0x01) {
				// bit is logical ZERO (0)
				if(l->zero_code_hole_color != NULL) {
					// we want to paint zeros.
					cairo_set_source(cr, l->zero_code_hole_color);
					cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
					cairo_fill(cr);
				}
			} else if(l->one_code_hole_color != NULL) {
				// we want to paint logical ONEs.
				cairo_set_source(cr, l->one_code_hole_color);
				cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
				cairo_fill(cr);
			}
			
			if(l->highlight_bit_row == row && l->highlight_bit_track == track
			   && l->highlight_bit_color != NULL) {
				// if check against l->highlight_bit_row == LOCHSTREIFEN_NO_ROW
				// not neccessary because row never gets LOCHSTREIFEN_NO_ROW.
				
				// eigentlich wollen wir hier eine Umrahmung zeichnen,
				// damit der Zustand (0/1) nicht unsichtbar wird. Jetzt
				// erst mal aber die Billigversion mit der dümmlichen
				// Vollfarbe ;-)
				cairo_set_source(cr, l->highlight_bit_color);
				cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
				cairo_set_line_width(cr, 0.01);
				cairo_stroke(cr); // mal testen :-)
			}
		} // for tracks (bits)
	} // for rows (bytes)
} // function lochstreifen_draw

