#include <grass/gis.h>
#include "format.h"
#include "local_proto.h"
#define debug stderr

struct Cell_head region, page;

static union
{
    char **c;
    unsigned char **u;
    short **s;
    CELL **cell;
} raster;
static int max_rows;
static int at_row;
static long cat;
static int cur_x, cur_y;
static int format;
static CELL *cell;

static int (*dot)();
static int cell_dot (int,int);
static int uchar_dot (int,int);
static int char_dot (int,int);
static int short_dot (int,int);
static int move (int,int);
static int cont (int,int);

int begin_rasterization (int nrows, int f)
{
    int i,size;
    int pages;

    format = f;

    max_rows = nrows;
    if (max_rows <= 0)
	max_rows = 512;

    G_get_set_window (&region);
    G_get_set_window (&page);

    pages = (region.rows + max_rows - 1) / max_rows;

    if (max_rows > region.rows)
	max_rows = region.rows;

    size = max_rows * region.cols;
    switch (format)
    {
    case USE_CHAR:
	raster.c = (char **) G_calloc (max_rows, sizeof (char *));
	raster.c[0] = (char *) G_calloc (size, sizeof(char));
	for (i = 1; i < max_rows; i++)
	    raster.c[i] = raster.c[i-1] + region.cols;
	dot = char_dot;
	break;

    case USE_UCHAR:
	raster.u = (unsigned char **) G_calloc (max_rows, sizeof (unsigned char *));
	raster.u[0] = (unsigned char *) G_calloc (size, sizeof(unsigned char));
	for (i = 1; i < max_rows; i++)
	    raster.u[i] = raster.u[i-1] + region.cols;
	dot = uchar_dot;
	break;

    case USE_SHORT:
	raster.s = (short **) G_calloc (max_rows, sizeof (short *));
	raster.s[0] = (short *) G_calloc (size, sizeof(short));
	for (i = 1; i < max_rows; i++)
	    raster.s[i] = raster.s[i-1] + region.cols;
	dot = short_dot;
	break;

    case USE_CELL:
	raster.cell = (CELL **) G_calloc (max_rows, sizeof (CELL *));
	raster.cell[0] = (CELL *) G_calloc (size, sizeof(CELL));
	for (i = 1; i < max_rows; i++)
	    raster.cell[i] = raster.cell[i-1] + region.cols;
	dot = cell_dot;
	break;
    }
    if (format != USE_CELL)
	cell = G_allocate_cell_buf();

    at_row = 0;
    configure_plot();

    return pages;
}

#define DONE 1
#define ERROR -1
#define AGAIN 0

int configure_plot (void)
{
    int i,j;
    int nrows;
    int ncols;

    nrows = region.rows - at_row;
    if (nrows <= 0)
	return DONE;

    if (nrows > max_rows)
	nrows = max_rows;
    
    ncols = region.cols;

/* zero the raster */
    switch (format)
    {
    case USE_CHAR:
	for (i = 0; i < nrows; i++)
	    for (j = 0; j < ncols; j++)
		raster.c[i][j] = 0;
	break;

    case USE_UCHAR:
	for (i = 0; i < nrows; i++)
	    for (j = 0; j < ncols; j++)
		raster.u[i][j] = 0;
	break;

    case USE_SHORT:
	for (i = 0; i < nrows; i++)
	    for (j = 0; j < ncols; j++)
		raster.s[i][j] = 0;
	break;

    case USE_CELL:
	for (i = 0; i < nrows; i++)
	    for (j = 0; j < ncols; j++)
		raster.cell[i][j] = 0;
	break;
    }

/* change the region */
    page.north = region.north - at_row * region.ns_res;
    page.south = page.north - nrows * region.ns_res;
    G_set_window (&page);

/* configure the plot routines */
    G_setup_plot (-0.5, page.rows-0.5, -0.5, page.cols-0.5, move, cont);

    return AGAIN;
}

int output_raster (int fd)
{
    int i,j;

    for (i = 0; i < page.rows; i++, at_row++)
    {
	switch (format)
	{

	case USE_CHAR:
	    for (j = 0; j < page.cols; j++)
	    {
		cell[j] = (CELL) raster.c[i][j];
		if (cell[j] == 0)
			G_set_null_value(&cell[j],1,CELL_TYPE);
	    }
	    break;

	case USE_UCHAR:
	    for (j = 0; j < page.cols; j++)
	    {
		cell[j] = (CELL) raster.u[i][j];
		if (cell[j] == 0)
			G_set_null_value(&cell[j],1,CELL_TYPE);
	    }
	    break;

	case USE_SHORT:
	    for (j = 0; j < page.cols; j++)
	    {
		cell[j] = (CELL) raster.s[i][j];
		if (cell[j] == 0)
			G_set_null_value(&cell[j],1,CELL_TYPE);
	    }
	    break;

	case USE_CELL:
	    cell = raster.cell[i];
	    if (cell == 0)
			G_set_null_value(&cell,1,CELL_TYPE);
	    break;
	}

	G_percent (i, page.rows, 2);
	if (G_put_raster_row (fd, cell, CELL_TYPE) < 0)
	    return ERROR;
    }
    G_percent (i, page.rows, 2);
    return configure_plot();
}

int set_cat (long x)
{
    cat = x;

    return 0;
}

int raster_dot (int x, int y)
{
    dot(x,y);

    return 0;
}

static int move (int x, int y)
{
    cur_x = x;
    cur_y = y;

    return 0;
}

static int cont (int x, int y)
{
    if(cur_x < 0 && x < 0) goto set;
    if(cur_y < 0 && y < 0) goto set;
    if(cur_x >= page.cols && x >= page.cols) goto set;
    if(cur_y >= page.rows && y >= page.rows) goto set;

    G_bresenham_line (cur_x, cur_y, x, y, dot);

set:
    move (x, y);

    return 0;
}

static int cell_dot (int x, int y)
{
    if (x >= 0 && x < page.cols && y >= 0 && y < page.rows)
	raster.cell[y][x] = cat;

    return 0;
}

static int uchar_dot (int x, int y)
{
    if (x >= 0 && x < page.cols && y >= 0 && y < page.rows)
	raster.u[y][x] = cat;

    return 0;
}

static int char_dot (int x, int y)
{
    if (x >= 0 && x < page.cols && y >= 0 && y < page.rows)
	raster.c[y][x] = cat;

    return 0;
}

static int short_dot (int x, int y)
{
    if (x >= 0 && x < page.cols && y >= 0 && y < page.rows)
	raster.s[y][x] = cat;

    return 0;
}
