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

#include "../../src/util/error.h"
#include "../../src/util/memory.h"

#define ps_IMPORT
#include "ps.h"

struct ps_Type {
	
	/**
	 * Current line width (pt). Here we check the current set value to avoid
	 * redundant changes of the setting, making the generated PS file shorter.
	 */
	double curr_line_width;

	/**
	 * Current gray level [0,1]. 0 = black, 1 = white.  Here we check the
	 * current set value to avoid redundant changes of the setting, making the
	 * generated PS file shorter.
	 */
	double curr_gray_level;
};


ps_Type * ps_new()
{
	ps_Type * this;

	this = memory_allocate(sizeof(ps_Type), NULL);
	this->curr_gray_level = 0;
	this->curr_line_width = 1;
	
	printf("%%!PS-Adobe-2.0\n");
	
	return this;
}


void ps_setClipRect(ps_Type *this, double x, double y, double w, double h)
{
	printf("newpath\n");
	ps_moveTo(this, x, y);
	ps_relativeLineTo(this, w, 0);
	ps_relativeLineTo(this, 0, h);
	ps_relativeLineTo(this, -w, 0);
	ps_relativeLineTo(this, 0, -h);
	printf("closepath\n");
	printf("clip\n");
}


void ps_setFontHeight(ps_Type *this, int h)
{
	printf("/Helvetica findfont %d scalefont setfont\n", h);
}


void ps_setLineWidth(ps_Type *this, double w)
{
	w = (int)(8 * w + 0.5) / 8.0;
	if( w == this->curr_line_width )
		return;
	this->curr_line_width = w;
	printf("%.1f setlinewidth\n", w);
}


void ps_moveTo(ps_Type *this, double x, double y)
{
	printf("%.1f %.1f moveto\n", x, y);
}


/*
void rmoveto(ps_Type *this, double x, double y)
{
	printf("%.1f %.1f rmoveto\n", x, y);
}
*/


void ps_stroke(ps_Type *this)
{
	printf("stroke\n");
}


void ps_lineTo(ps_Type *this, double x, double y)
{
	printf("%.1f %.1f lineto\n", x, y);
}


void ps_drawLine(ps_Type *this, double x1, double y1, double x2, double y2)
{
	ps_moveTo(this, x1, y1);
	ps_lineTo(this, x2, y2);
	printf("stroke\n");
}


void ps_relativeLineTo(ps_Type *this, double x, double y)
{
	printf("%.1f %.1f rlineto\n", x, y);
}


void ps_setGray(ps_Type *this, double brightness)
{
	if( !(0 <= brightness && brightness <= 1.0) )
		error_internal("gray brightness out of the range: %g", brightness);
	brightness = (int)(brightness * 256) / 256.0;
	if( brightness == this->curr_gray_level )
		return;
	this->curr_gray_level = brightness;
	printf("%.3f setgray\n", brightness); 
}


void ps_drawRect(ps_Type *this, double x, double y, double w, double h)
{
	printf("newpath\n");
	printf("%.1f %.1f moveto\n", x, y);
	printf("%.1f 0 rlineto\n", w);
	printf("0 %.1f rlineto\n", h);
	printf("%.1f 0 rlineto\n", -w);
	printf("0 %.1f rlineto\n", -h);
	printf("closepath\n");
	printf("stroke\n");
}


void ps_fillRect(ps_Type *this, double x, double y, double w, double h)
{
	printf("newpath\n");
	printf("%.1f %.1f moveto\n", x, y);
	printf("%.1f 0 rlineto\n", w);
	printf("0 %.1f rlineto\n", h);
	printf("%.1f 0 rlineto\n", -w);
	printf("0 %.1f rlineto\n", -h);
	printf("closepath\n");
	printf("fill\n");
}


/**
 * Draws a rectangular white box with black border.
 */
void ps_drawFramedRect(ps_Type *this, double x, double y, double w, double h)
{
	ps_setGray(this, 1.0);
	ps_fillRect(this, x, y, w, h);
	ps_setGray(this, 0.0);
	ps_setLineWidth(this, 1);
	ps_moveTo(this, x, y);
	ps_drawRect(this, x, y, w, h);
	//printf("stroke\n");
}


void ps_drawCircumference(ps_Type *this, double x, double y, double r)
{
	printf("newpath\n");
	printf("%.1f %.1f %.1f 0 360 arc\n", x, y, r);
	printf("closepath\n");
	printf("stroke\n");
}


void ps_drawCircle(ps_Type *this, double x, double y, double r)
{
	printf("newpath\n");
	printf("%.1f %.1f %.1f 0 360 arc\n", x, y, r);
	printf("closepath\n");
	printf("fill\n");
}


/*
static int indexOfChar(char *s, int c)
{
	char *found = strchr(s, c);
	if( found == NULL )
		return -1;
	else
		return found - s;
}
*/


static int indexOfSubstring(char *s, char *target)
{
	char *found = strstr(s, target);
	if( found == NULL )
		return -1;
	else
		return found - s;
}


/**
 * Print ASCII printable only string properly PostScript escaped.
 * @param s
 */
void ps_printEscapedString(ps_Type *this, char *s)
{
	putchar('(');
	while(*s){
		char c = *s;
		if( c == '(' ){
			puts("\\(");
		} else if( c == ')' ){
			puts("\\)");
		} else if( c == '\\' ){
			puts("\\");
		} else if( 32 <= c && c <= 126 ){
			putchar(c);
		} else {
			fprintf(stderr, "Warning: non-ASCII printable code %d in string\n", c & 255);
			putchar('?');
		}
		s++;
	}
	printf(") show\n");
}


typedef struct {
	char *name;
	char *glyph;
} ratnest_CharEntity;


static ratnest_CharEntity entities[] = {
	{"&degree;", "/degree"},
	{"&multiply;", "/multiply"},
	{NULL, NULL}
};


/**
 * Draws a string at the current location. To display the degree symbol,
 * caller may use the pseudo-HTML entity "&degree;" instead which is the
 * only one handled specially.
 * @param s ASCII printable chars only supported.
 */
void ps_drawString(ps_Type *this, char *s)
{
	while( *s != 0 ){
		int found_index = -1;
		ratnest_CharEntity *found_entity = entities;
		while(found_entity->name != NULL){
			found_index = indexOfSubstring(s, found_entity->name);
			if( found_index >= 0 )
				break;
			found_entity++;
		}
		if( found_index < 0 ){
			ps_printEscapedString(this, s);
			return;
		}
		// Print chunk from beginning up to the entity (excluded):
		s[found_index] = 0;
		//ps_printEscapedString(this, s);
		ps_drawString(this, s); // recursive!
		s[found_index] = found_entity->name[0];
		// Print the symbol:
		printf("%s glyphshow\n", found_entity->glyph);
		// Move past the entity:
		s = s + found_index + strlen(found_entity->name);
	}
}


void ps_close(ps_Type *this)
{
	printf("showpage\n");
}
