#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* Digit style constants */
const double segment_gap=0.03; /* Part of height */
const double segment_width=0.07; /* Part of height */
const double width=0.52; /* width/height */
const double space=0.3; /* space/height */
const double point=0.15; /* Point diameter */

/* Dash line style */
double dash_slot=1.5; /* Length of black and also length of white slot */

double lastx, lasty;
int style; /* 1=dash */

/* Converts mm to points (1/72 of inch, which is 25.4mm) */
static double pt(double mm)
{
	return mm/25.4*72;
}

static void thickness (double mm)
{
	printf("stroke\n%.12G setlinewidth\n",pt(mm));
}

static void solid(void)
{
	style=0;
	thickness(0.3);
}

static void bend(void)
{
	style=1;
	thickness(0.2);
}

static void helper(void)
{
	style=0;
	thickness(0.1);
}

/* Doesn't set the lastx and lasty */
static void solid_line(double x1, double y1, double x2, double y2)
{
	printf("%.12G %.12G moveto %.12G %.12G lineto\n",pt(x1), pt(y1),
			pt(x2), pt(y2));
}

static void dashed_line(double x1, double y1, double x2, double y2)
{
	int ndash;
	double dx=x2-x1;
	double dy=y2-y1;
	double len;

	len=sqrt(dx*dx+dy*dy);
	ndash=floor(len/dash_slot);
	ndash|=1; /* Make even */
	if (ndash<3) ndash=3;

	/* Now calculate the step */
	dx/=ndash;
	dy/=ndash;
	for (;ndash>0;ndash-=2){
		solid_line(x1, y1, x1+dx, y1+dy);
		x1+=2*dx;
		y1+=2*dy;
	}
}

static void line(double x1, double y1, double x2, double y2){
	if (style) dashed_line(x1, y1, x2, y2);
	else solid_line(x1, y1, x2, y2);
	lastx=x2;
	lasty=y2;
}

/* Input is relative to the last point */
static void rline(double dx, double dy)
{
	line(lastx, lasty, lastx+dx, lasty+dy);
}

static void moveto(double dx, double dy)
{
	lastx=dx;
	lasty=dy;
}

static void rmove(double dx, double dy)
{
	lastx+=dx;
	lasty+=dy;
}

static void epilogue(void)
{
	printf("stroke\nshowpage\n");
}

/* Invert must be either 1 (left part) or -1 (right part) */
static void stand(double rd, int i)
{
	double height=10;
	bend();
	rline(-i*(10+rd), 0);
	rmove(i*(10+rd), 0);
	solid();

	if (rd>=0){
		rline(-5*i,-5);
		rline(-rd*i, -0);
		rline(-5*i, -5);
	}else{
		height=10+rd;
		rline(-i*(10+rd),-height);
	}

	rline (0,height);
	rline(-3*i, 5);
	rline(3*i,5);
	rline (0,height);

	if (rd>=0){
		rline(5*i,-5);
		rline(rd*i,0);
		rline(5*i,-5);
	}else{
		rline(i*(10+rd),-height);
	}

	bend();
	rline(-i*(10+rd), 0);
	rmove(i*(10+rd), 0);
	solid();

}

/* Makes a gap set up by segment_gap% of the length. The thickness of segment is
 * given by segment_width. height is used to calculate the width and gap. */
static void segment (double x1, double y1, double x2, double y2, double height)
{
	double dx, dy;
	double gapx, gapy, widthx, widthy;
	double rx, ry, lx, ly;
	double len;

	dx=x2-x1;
	dy=y2-y1;
	len=sqrt(dx*dx+dy*dy);

	{
		double gap_len=height*segment_gap;
		gapx=dx*gap_len/len;
		gapy=dy*gap_len/len;
	}
	{
		double width=height*segment_width;
		widthx=dx*width/len;
		widthy=dy*width/len;
	}
	rx=widthx-widthy+gapx; /* Vectors summed with perpendicular */
	ry=widthy+widthx+gapy;
	lx=widthx+widthy+gapx;
	ly=widthy-widthx+gapy;

	printf("stroke\n"
			"newpath\n"
			"%.12G %.12G moveto\n"
			"%.12G %.12G lineto\n"
			"%.12G %.12G lineto\n"
			"%.12G %.12G lineto\n"
			"%.12G %.12G lineto\n"
			"%.12G %.12G lineto\n"
			"%.12G %.12G lineto\n"
			"closepath\n"
			"fill\n"
			,pt(x1+gapx), pt(y1+gapy)
			,pt(x1+rx), pt(y1+ry)
			,pt(x2-lx), pt(y2-ly)
			,pt(x2-gapx), pt(y2-gapy)
			,pt(x2-rx), pt(y2-ry)
			,pt(x1+lx), pt(y1+ly)
			,pt(x1+gapx), pt(y1+gapy));

	

}

double digit_width(double height, unsigned char v)
{
	if (v=='.')
		return height*(point+space);
	else if (v>='0'&&v<='9')	
		return height*(width+segment_width+space);
	else return 0;
}

/* v: 0-9
 * Returns amount of x shift */
double digit (double x, double y, double height, unsigned char input_char)
{
	double w;
	static const unsigned char table[]={
		/* 0     1     2     3     4 */
		0x3f, 0x03, 0x6d, 0x67, 0x53, 
		/* 5     6     7     8     9*/
		0x76, 0x7e, 0x23, 0x7f, 0x77,
	};

	/* Bottom segment */
	if (input_char=='.'){
		double r=height*point/2;
		printf("stroke\n"
			"%.12G %.12G moveto\n"
			"%.12G %.12G %.12G 0 360 arc\n"
			"fill\n"
			,pt(x), pt(y+r)
			,pt(x+r), pt(y+r), pt(r));
		/* Dot */
	}else if (input_char>='0'&&input_char<='9'){
		unsigned char v=table[input_char-'0'];
		/* Digit */
		w=height*width;
		if (v&1) segment(x+w, y+height/2, x+w, y+height, height);
		if (v&2) segment(x+w, y, x+w, y+height/2, height);
		if (v&4) segment(x, y, x+w, y, height);
		if (v&8) segment(x, y, x, y+height/2, height);
		if (v&16) segment(x, y+height/2, x, y+height, height);
		if (v&32) segment(x, y+height, x+w, y+height, height);
		if (v&64) segment(x, y+height/2, x+w, y+height/2, height);
	} 
	return digit_width(height, input_char);
}

/* All in mm */
void string(double x, double y, double height, unsigned char *str)
{
	unsigned char c;

	while((c=*str++)) x+=digit(x, y, height, c);
}

/* In mm */
double string_width(double height, unsigned char *str)
{
	double width=0;
	unsigned char c;

	while((c=*str++))
		width+=digit_width(height, c);
	return width;
}

#define URIGHT 0
#define ULEFT 1
#define LLEFT 2
#define LRIGHT 3
/* off says the horizontal or vertical (both the same) component of the offset
 * from the lastx,lasty (hole middle). */
void dia_label(double off, double height, double dia, int pos)
{
	unsigned char txt[256];
	int xoff, yoff;

	snprintf(txt, sizeof(txt), "%.12G", dia);
	switch (pos)
	{
		case URIGHT: xoff=off; yoff=off; break;
		case ULEFT: xoff=-off-string_width(height, txt); yoff=off; break;
		case LLEFT: xoff=-off-string_width(height, txt);
			    yoff=-off-height; break;
		default: xoff=off; yoff=-off-height; break; /* LRIGHT */
	}
	string(lastx+xoff, lasty+yoff, height, txt);
}

/* Sets the style to solid */
/* pos: URIGHT, ULEFT, LLEFT, LRIGHT */
static void hole(double dia, char pos)
{
	double r=dia/2;
	solid();
	printf("%.12G %.12G moveto\n",pt(lastx+r), pt(lasty));
	printf("%.12G %.12G %.12G 0 360 arc\n",pt(lastx),
			pt(lasty), pt(r));
	dia_label(r*0.75, dia, dia, pos);
}

void prolog(void)
{
	printf("%%!PS-Adobe\n");
}

int main(int argc, char **argv)
{
	double rd; /* Reduced depth */
	int i;
	double depth, nominal;

	if (argc<2){
		fprintf(stderr,"Usage: plot <difference>\n\n"
				"Where difference is the difference between "
				"the tin box outer dimension and pipe "
				"inner dimension.\n");
		exit(1);
	}
	depth=strtod(argv[1], NULL);
	nominal=depth;
	depth/=2; /* We have the same gap on both sides */
	depth-=3; /* Something to accomodate for imprecisions. Something
		     because the fastening element has some width and the
		     pipe is curved. */

	prolog();
	for (i=0;i<4;i++){
		static unsigned char str[256];
		solid();

		rd=depth-10;
		
		moveto(71.5,100+30*i);

		/* Central part */
		rline(3,-3);
		rline(61,0);
		rline(3,3);
		bend();
		rline(0,10);
		solid();
		rline(-3, 3);
		rline(-61,0 );
		rline(-3, -3);
		bend();
		rline(0, -10);
		solid();

		stand(rd, 1);
		rmove(67,-10);
		stand(rd, -1);
		rmove (0,-5);
		helper();
		rline(-67,0);
		rmove(8,8);
		rline(0,-16);
		rmove(51,0);
		rline(0,16);
		rmove(0,-8);
		hole(4.5, ULEFT);
		rmove(-51,0);
		hole(4.5, URIGHT);
		snprintf(str,sizeof(str),"%.12G",nominal);
		string (75, 230, 20, str);
	}

	epilogue();
	return 0;
}
