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

#include "svg.h"

#define USE_ACTUAL 1		// use dimensions inferred from drawing
				// rather than fixed 24x24in from Corel
				// template...

static void appendstringtosvg (svg * psvg, char *text) {
   int l = strlen (psvg->svg) + strlen (text) + 1;
   char *p = realloc (psvg->svg, l);	// unoptimal way to extend strings...

   if (p) psvg->svg = p;
   strcat (psvg->svg, text);
}

static void appendnumbertosvg (svg * psvg, int n) {
   char sn[16];

   sprintf (sn, "%0d", n * 100 / 254);	// mm to in
   appendstringtosvg (psvg, sn);
}

static void appenddecimalnumbertosvg (svg * psvg, int n) {
   char sn[16];

   sprintf (sn, "%0.3f", (float) n / 2540.0);	// mm to in - maybe a small
						// rounding issue...
   appendstringtosvg (psvg, sn);
}

svg *svg_create (int width, int height) {
   svg *psvg = malloc (sizeof (svg));

   if (psvg != NULL) {
      *psvg = (svg) {
        .svg = NULL,
	.width = width,
	.height = height,
	.finalized = false
      };
      psvg->svg = malloc (1);
      psvg->svg[0] = '\0';
      appendstringtosvg (psvg,
			 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
      appendstringtosvg (psvg,
			 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
      appendstringtosvg (psvg, "<!-- Creator: CorelDRAW X7 -->\n");
      appendstringtosvg (psvg,
			 "<svg xmlns=\"http://www.w3.org/2000/svg\" xml:space=\"preserve\" width=\"");
#ifdef USE_ACTUAL
      appenddecimalnumbertosvg (psvg, width);
#else
      appendstringtosvg (psvg, "24");
#endif
      appendstringtosvg (psvg, "in\" height=\"");
#ifdef USE_ACTUAL
      appenddecimalnumbertosvg (psvg, height);
#else
      appendstringtosvg (psvg, "24");
#endif
      appendstringtosvg (psvg,
			 "in\" version=\"1.1\" style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\"");
      appendstringtosvg (psvg, " viewBox=\"0 0 ");
#ifdef USE_ACTUAL
      appendnumbertosvg (psvg, width);
      appendstringtosvg (psvg, " ");
      appendnumbertosvg (psvg, height);
#else
      appendstringtosvg (psvg, "24000 24000");
#endif
      appendstringtosvg (psvg, "\"");
      appendstringtosvg (psvg,
			 " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n");
      appendstringtosvg (psvg, " <defs>\n");
      appendstringtosvg (psvg, "  <style type=\"text/css\">\n");
      appendstringtosvg (psvg, "   <![CDATA[\n");
      appendstringtosvg (psvg, "    .str0 {stroke:red}\n");
      appendstringtosvg (psvg, "    .str1 {stroke:black}\n");
      appendstringtosvg (psvg, "    .fil0 {fill:none}\n");
      appendstringtosvg (psvg, "   ]]>\n");
      appendstringtosvg (psvg, "  </style>\n");
      appendstringtosvg (psvg, " </defs>\n");
      appendstringtosvg (psvg, " <g id=\"Layer_x0020_1\">\n");
      appendstringtosvg (psvg,
			 "  <metadata id=\"CorelCorpID_0Corel-Layer\"/>\n");

      return psvg;
   } else {
      return NULL;
   }
}

void svg_finalize (svg * psvg) {
   appendstringtosvg (psvg, "  </g>\n");
   appendstringtosvg (psvg, "</svg>\n");
   psvg->finalized = true;
}

void svg_print (svg * psvg) {
   printf ("%s\n", psvg->svg);
}

void svg_save (svg * psvg, char *filepath) {
   FILE *fp;

   if (!psvg->finalized) svg_finalize (psvg);

   fp = fopen (filepath, "w");
   if (fp != NULL) {
      fwrite (psvg->svg, 1, strlen (psvg->svg), fp);
      fclose (fp);
   }
}

void svg_free (svg * psvg) {
   free (psvg->svg);
   free (psvg);
}

void svg_circle (svg * psvg, char *stroke, int strokewidth, char *fill, int r,
		 int cx, int cy)
{
   appendstringtosvg (psvg, "    <circle stroke=\"");
   appendstringtosvg (psvg, stroke);
   appendstringtosvg (psvg, "\" stroke-width=\"");
   appendnumbertosvg (psvg, strokewidth);
   appendstringtosvg (psvg, "\" fill=\"");
   appendstringtosvg (psvg, fill);
   appendstringtosvg (psvg, "\" r=\"");
   appendnumbertosvg (psvg, r);
   appendstringtosvg (psvg, "\" cy=\"");
   appendnumbertosvg (psvg, cy);
   appendstringtosvg (psvg, "\" cx=\"");
   appendnumbertosvg (psvg, cx);
   appendstringtosvg (psvg, "\" />\n");
}

void svg_line (svg * psvg, char *stroke, int strokewidth, int x1, int y1,
	       int x2, int y2) {
   if ((strokewidth <= 1) && (strcmp (stroke, "#FF0000") == 0)) {
      appendstringtosvg (psvg, "    <line class=\"fil0 str0\" ");
   } else {
      appendstringtosvg (psvg, "    <line stroke=\"");
      appendstringtosvg (psvg, stroke);
      appendstringtosvg (psvg, "\" stroke-width=\"");
      appendnumbertosvg (psvg, strokewidth);
      appendstringtosvg (psvg, "\" ");
   }
   appendstringtosvg (psvg, " x1=\"");
   appendnumbertosvg (psvg, x1);
   appendstringtosvg (psvg, "\" y1=\"");
   appendnumbertosvg (psvg, y1);
   appendstringtosvg (psvg, "\" x2=\"");
   appendnumbertosvg (psvg, x2);
   appendstringtosvg (psvg, "\" y2=\"");
   appendnumbertosvg (psvg, y2);

   appendstringtosvg (psvg, "\" />\n");
}

void svg_rectangle (svg * psvg, int width, int height, int x, int y,
		    char *fill, char *stroke, int strokewidth, int radiusx,
		    int radiusy) {
   appendstringtosvg (psvg, "    <rect");

   if (   (strcmp (fill, "#FFFFFF") == 0)
       && (strokewidth <= 1)
       && (strcmp (stroke, "#FF0000") == 0)) {
      appendstringtosvg (psvg, " class=\"fil0 str0\"");
   } else {
      appendstringtosvg (psvg, " fill=\"");
      appendstringtosvg (psvg, fill);
      appendstringtosvg (psvg, "\"");
// appendstringtosvg(psvg, " fill-opacity=\"0.0\"");
      appendstringtosvg (psvg, " stroke=\"");
      appendstringtosvg (psvg, stroke);
      appendstringtosvg (psvg, "\" stroke-width=\"");
      appendnumbertosvg (psvg, strokewidth);
      appendstringtosvg (psvg, "\"");
   }

   appendstringtosvg (psvg, " x=\"");
   appendnumbertosvg (psvg, x);

   appendstringtosvg (psvg, "\" y=\"");
   appendnumbertosvg (psvg, y);

   appendstringtosvg (psvg, "\" width=\"");
   appendnumbertosvg (psvg, width);
   appendstringtosvg (psvg, "\" height=\"");
   appendnumbertosvg (psvg, height);

   if ((radiusx != 0) && (radiusy != 0)) {
      appendstringtosvg (psvg, "\" rx=\"");
      appendnumbertosvg (psvg, radiusx);

      appendstringtosvg (psvg, "\" ry=\"");
      appendnumbertosvg (psvg, radiusy);
   }

   appendstringtosvg (psvg, "\" />\n");
}

void svg_fill (svg * psvg, char *Fill) {
   svg_rectangle (psvg, psvg->width, psvg->height, 0, 0, Fill, Fill, 0, 0, 0);
}

void svg_text (svg * psvg, int x, int y, char *fontfamily, int fontsize,
	       char *fill, char *stroke, char *text, char *transform) {
   appendstringtosvg (psvg, "    <text x=\"");
   appendnumbertosvg (psvg, x);
   appendstringtosvg (psvg, "\" y = \"");
   appendnumbertosvg (psvg, y);
   appendstringtosvg (psvg, "\" font-family=\"");
   appendstringtosvg (psvg, fontfamily);
   appendstringtosvg (psvg, "\" stroke=\"");
   appendstringtosvg (psvg, stroke);
   appendstringtosvg (psvg, "\" fill=\"");
   appendstringtosvg (psvg, fill);
   appendstringtosvg (psvg, "\" font-size=\"");
   appendnumbertosvg (psvg, fontsize);
   appendstringtosvg (psvg, "\"");
   appendstringtosvg (psvg, transform);
   appendstringtosvg (psvg, ">");
   appendstringtosvg (psvg, text);
   appendstringtosvg (psvg, "</text>\n");
}

void svg_ellipse (svg * psvg, int cx, int cy, int rx, int ry, char *fill,
		  char *stroke, int strokewidth) {
   appendstringtosvg (psvg, "    <ellipse cx=\"");
   appendnumbertosvg (psvg, cx);
   appendstringtosvg (psvg, "\" cy=\"");
   appendnumbertosvg (psvg, cy);
   appendstringtosvg (psvg, "\" rx=\"");
   appendnumbertosvg (psvg, rx);
   appendstringtosvg (psvg, "\" ry=\"");
   appendnumbertosvg (psvg, ry);
   appendstringtosvg (psvg, "\" fill=\"");
   appendstringtosvg (psvg, fill);
   appendstringtosvg (psvg, "\" stroke=\"");
   appendstringtosvg (psvg, stroke);
   appendstringtosvg (psvg, "\" stroke-width=\"");
   appendnumbertosvg (psvg, strokewidth);
   appendstringtosvg (psvg, "\" />\n");
}