/**
***	Author:		Rich Franzen
***	Date:		27 February 1993
***	Version:	@(#)SquareSnake.c	1.3	94/08/20
***
***   This program creates a 12-bit image.  It is a 64*64-block grid with
*** intensities beginning to 0 and ending at 4095 as the pattern spirals
*** clockwise to the center.  Each block is a 5x5-pixel element, with the
*** upper-left 4x4 being the proper intensity, and the right and bottom
*** sides being black.
***
***   (20aug94rwf) Extended program to optionally build a 16-bit image
*** in a 256x256 array.
**/


#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define	NULL	0

#define	CHECKERW_4k	64
#define	CHECKERH_4k	64
#define	BLOCKW_4k	 5
#define	BLOCKH_4k	 5
#define	blockW_4k	 4
#define	blockH_4k	 4
#define	IMAGENAME_4k	"4ksnake.img"

#define	CHECKERW_64k	256
#define	CHECKERH_64k	256
#define	BLOCKW_64k	  4
#define	BLOCKH_64k	  4
#define	blockW_64k	  3
#define	blockH_64k	  3
#define	IMAGENAME_64k	"64ksnake.img"

#define	WIDTH		(CHECKERW * BLOCKW)
#define	HEIGHT		(CHECKERH * BLOCKH)

/**
***   These global variables have the capitalizations of the original
*** 4k-only form of this program.
**/
static long	CHECKERW	= CHECKERW_4k;
static long	CHECKERH	= CHECKERH_4k;
static long	BLOCKW		= BLOCKW_4k;
static long	BLOCKH		= BLOCKH_4k;
static long	blockW		= blockW_4k;
static long	blockH		= blockH_4k;
static char	IMAGENAME[16]	= IMAGENAME_4k;

	u_short	*image;

	enum	dirs { right, down, left, up };


/**
***   Procedure imbed() places a small block of the desired intensity
*** at block coordinates (col, row).
**/
void imbed(int intensity, int row, int col)
{
    int		skipw, skiph;
    int		maxw, maxh;
    int		x, y;
    long	yOffset;

    skipw = col   * BLOCKW;
    maxw  = skipw + blockW;
    skiph = row   * BLOCKH;
    maxh  = skiph + blockH;

    for (y = skiph; y < maxh; y++)
    {
	yOffset = y * WIDTH;
	for (x = skipw; x < maxw; x++)
	    image[yOffset + x] = intensity;
    }
}


int main(int argc, char *argv[])
{
    int		row, col, intensity;
    int		maxRight, maxDown, minLeft, minUp;
    int		fd, status;
    long	offset;
    long	imageSize;
    enum dirs	direction;
    int		background = 0;
    static char	*SccsId = "@(#)SquareSnake.c	1.3	94/08/20";

    printf("\nSquare Snake Generator, (c) 1993, 1994 Rich Franzen\n");

    if (argc > 1)
    {
	if (atoi(argv[1]) == 64)
	{
	    CHECKERW	  = CHECKERW_64k;
	    CHECKERH	  = CHECKERH_64k;
	    BLOCKW	  = BLOCKW_64k;
	    BLOCKH	  = BLOCKH_64k;
	    blockW	  = blockW_64k;
	    blockH	  = blockH_64k;
	    strcpy(IMAGENAME, IMAGENAME_64k);
	}
	  else if ((argv[1][1] | 0x20) == 'h')
	  {
	    printf("  Usage:");
	    printf("\n    %s [64 | 4 | -h] [background_intensity]", argv[0]);
	    printf("\n      64 yields a 64k snake '%s'", IMAGENAME_64k);
	    printf("\n       4 yields a 4k snake '%s' (default)", IMAGENAME_4k);
	    printf("\n      -h prints this usage message");
	    printf("\n      background_intensity must be 2nd parameter");
	    printf("\n         (if present) (default = 0)\n\n");
	    exit(0);
	  }

	if (argc > 2)
	    background = atoi(argv[2]);
    }

    imageSize = WIDTH * HEIGHT * sizeof(u_short);
    image = (u_short *)malloc(imageSize);
    if (image == NULL)
    {
	printf("\nUnable to malloc() %d bytes of memory for the image!\n",
	    WIDTH * HEIGHT * sizeof(u_short));
	exit(3);
    }

    /*  begin by filling image with background intensity  */
    printf("Clearing image[][] to intensity of %d\n", background);
    for (offset = 0; offset < (WIDTH * HEIGHT); offset++)
	image[offset] = background;

    /**
    ***   Establish initial values of our snaking
    *** state machine
    **/
    maxRight = CHECKERW;
    maxDown  = CHECKERH;
    minLeft  = -1;
    minUp    =  0;
    direction = right;
    row = col = 0;
    intensity = 0;

    /*  now fill in our checkerboard  */
    printf("Filling in the checkerboard...\n");
    while (intensity < (CHECKERW * CHECKERH))
    {
	imbed(intensity, row, col);
	switch (direction)
	{
	  case right:
	    col += 1;
	    if (col >= maxRight)
	    {
		col -= 1;
		maxRight -= 1;
		direction = down;
		row += 1;
	    }
	    break;
	  case down:
	    row += 1;
	    if (row >= maxDown)
	    {
		row -= 1;
		maxDown -= 1;
		direction = left;
		col -= 1;
	    }
	    break;
	  case left:
	    col -= 1;
	    if (col <= minLeft)
	    {
		col += 1;
		minLeft += 1;
		direction = up;
		row -= 1;
	    }
	    break;
	  case up:
	    row -= 1;
	    if (row <= minUp)
	    {
		row += 1;
		minUp += 1;
		direction = right;
		col += 1;
	    }
	    break;
	}
	intensity += 1;
    }

    printf("Writing image[][] to file '%s'\n", IMAGENAME);
    fd = open(IMAGENAME, O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if (fd < 0)
    {
	printf("Unable to open '%s', errno = %d\n", IMAGENAME, errno);
	free(image);
	exit(2);
    }
    status = write(fd, image, imageSize);
    if (status < imageSize)
    {
	if (status > 0)
	    printf("Incomplete write to '%s', bytes = %d\n", IMAGENAME, status);
	  else
	    printf("Unable to write to '%s', errno = %d\n", IMAGENAME, errno);
	status = 1;
    }
      else
	status = 0;

    close(fd);
    free(image);
    exit(status);
}

