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

#include "gpc.h"
#include "svg.h"

// outside dimensions of tray - eventually these may be command-line parameters...

float wood = 4.65;

#define W 142 /* left-right */
//#define H 380 /* top-bottom */
#define H 10 /* top-bottom */
#define D 52  /* front-back */

// --------------------------------------------------------
int mm(int m) {
  return m*100;
}

int in(int m) {
  return m*2540;
}

int ft(int m) {
  return in(m*12);
}

int mm_f(float m) {
  return (int)roundf(m*100.0);
}

int in_f(float m) {
  return (int)roundf(m*2540.0);
}

int ft_f(float m) {
  return in_f(m*12.0);
}

#define RED "#FF0000"
#define GREEN "#00FF00"
#define BLUE "#0000FF"
#define PINK "#FFAAAA"
#define WHITE "#FFFFFF"
#define BLACK "#000000"
#define BACKGROUND "#DDDDFF"

#define ENGRAVE_TEXT BLACK
#define COMMENT_TEXT BLUE

#define HAIRLINE_f 0.01
#define VISIBLE_LINE_f 0.2

#define LIVE_FILL WHITE
#define PREVIEW_FILL PINK

static svg* psvg;

int PREVIEW;
// value of these depends on PREVIEW
float linewidth_f;
char *fill;
float stage_width = 0.0, stage_height = 0.0;

void hole_mm_f(float w, float h, float x, float y) {
  if (x+w > stage_width) stage_width = x+w;
  if (y+h > stage_height) stage_height = y+h;

  if (PREVIEW) {
    svg_rectangle(psvg, /* size */ mm_f(w), mm_f(h), /* at */ mm_f(x), mm_f(y),
                  BACKGROUND, /* outline */ BACKGROUND, /* border-width */mm_f(linewidth_f),
                  /* radius x,y */ 0,0);
  } else {
    svg_rectangle(psvg, /* size */ mm_f(w), mm_f(h), /* at */ mm_f(x), mm_f(y),
                  WHITE, /* outline */ RED, /* border-width */mm_f(linewidth_f),
                  /* radius x,y */ 0,0);
  }
}

void face(int w, int h, int x, int y) {
  if (x+w > stage_width) stage_width = x+w;
  if (y+h > stage_height) stage_height = y+h;

  svg_rectangle(psvg, /* size */ mm(w), mm(h), /* at */ mm(x), mm(y),
                fill, /* outline */ RED, /* border-width */mm_f(linewidth_f),
                /* radius x,y */ 0,0);
}

#ifdef NEVER
  svg_text(psvg, /* at */ mm(x+10), mm(y+25+1),
           "sans-serif",
           mm(3),
           /* fill colour */ COMMENT_TEXT, /* stroke colour */ COMMENT_TEXT, "5cm x 5cm square", "");
#endif

void toothcomb(float center_x, float center_y, int cuts, float spacing, int cut_depth) {
  int i;
  float start_x;

  if ((cuts & 1) == 0) {
    // even number of cuts
    start_x = center_x - spacing/2 - ((cuts-1)>>1)*spacing;
  } else {
    // odd number of cuts
    start_x = center_x             - (cuts>>1)*spacing;
  }

  for (i = 0; i < cuts; i++) {
    svg_line(psvg, RED, /* width */ mm_f(linewidth_f),
	     /* from */ mm_f(start_x), mm_f(center_y),
      /* to */ mm_f(start_x), mm_f(center_y+cut_depth));
    start_x += spacing;
  }
}

int xpos, ypos;

void front_face(int x, int y) {
  int order;
  int steps = (W - (wood + /* 10 + 10 +*/ wood))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5; ypos = y+5;

  for (order = 0; order <= 1; order++) { // preview actions then live actions
    // cut before the face falls out
    if (order == (PREVIEW&1)) {
      //hole_mm_f(wood, H/2, /* at */ x+5 /*+ wood*/, y+5 + H/2);
      //hole_mm_f(wood, H/2, /* at */ x+5 + W-wood/*-wood*/, y+5 + H/2);

      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos + H/2.0 - H/6.0);
      hole_mm_f(wood, H/3.0, /* at */ xpos + W-wood/*-wood*/, ypos + H/2.0 - H/6.0);

      //hole_mm_f(W - 20, H - 10, x+5+10, y+5+10);

      toothcomb(xpos + W/2.0, ypos, steps, 2.0, 2);
    }
    if (order == 0) face(W, H, xpos, ypos);
  }
}

void back_face(int x, int y) {
  int order;
  int steps = (W - (wood + /* 10 + 10 +*/ wood))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5/*+W+5*/; ypos = y+5+H+5;

  for (order = 0; order <= 1; order++) { // preview actions then live actions
    if (order == (PREVIEW&1)) {
      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos + H/2.0 - H/6.0);
      hole_mm_f(wood, H/3.0, /* at */ xpos + W-wood/*-wood*/, ypos + H/2.0 - H/6.0);

      //hole_mm_f(W - 20, H - 10, x+5+W+5+10, y+5+10);

      toothcomb(xpos + W/2.0, ypos, steps, 2.0, 2);
    }
    if (order == 0) face(W, H, xpos, ypos);
  }
}

void left_face(int x, int y) {
  int order;
  int steps = (D - (wood + /* 10 + 10 +*/ wood))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5/*+W+5+W+5*/; ypos = y+5+H+5+H+5;

  for (order = 0; order <= 1; order++) { // preview actions then live actions
    if (order == (PREVIEW&1)) {
      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos);
      hole_mm_f(wood, H/3.0, /* at */ xpos + D-wood/*-wood*/, ypos);

      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos - H/3.0 + H);
      hole_mm_f(wood, H/3.0, /* at */ xpos + D-wood/*-wood*/, ypos - H/3.0 + H);

      //toothcomb(x+5+W+5+W+5 + D/2.0, y+5, steps, 2, 3);
    }
    if (order == 0) face(D, H/*+10*/, xpos, ypos);
  }
}

void right_face(int x, int y) {
  int order;
  int steps = (D - (wood + /* 10 + 10 +*/ wood))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5 /*+W+5+W+5*/ +D+5; ypos = y+5+H+5+H+5;

  for (order = 0; order <= 1; order++) { // preview actions then live actions
    if (order == (PREVIEW&1)) {
      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos);
      hole_mm_f(wood, H/3.0, /* at */ xpos + D-wood/*-wood*/, ypos);

      hole_mm_f(wood, H/3.0, /* at */ xpos /*+ wood*/, ypos - H/3.0 + H);
      hole_mm_f(wood, H/3.0, /* at */ xpos + D-wood/*-wood*/, ypos - H/3.0 + H);

      //toothcomb(x+5+W+5+W+5+D+5 + D/2.0, y+5, steps, 2, 3);
    }
    if (order == 0) face(D, H/*+10*/, xpos, ypos);
  }
}

#define margin 7.0

void struts(int x, int y) {
  int order;
  int steps = (D - (wood + /* 10 + 10 +*/ wood))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5; ypos = y+5;

  for (order = 0; order <= 1; order++) { // preview actions then live actions
    if (order == (PREVIEW&1)) {
    }
    if (order == 0) {
      face(D, margin, xpos, ypos);
      face(D, margin, xpos+wood+D, ypos);
      face(D-margin-margin+2.0-0.6, 3.0, /* at */ xpos + D + wood + D + wood, ypos);
    }
  }
}

void base(int x, int y) {
  //  face(D, W, x+5+W+5+W+5+D+5+D+5, y+5);
  int order;
  int steps = (W - (margin + /* 10 + 10 +*/ margin))/2 ; // NO /*1 cm*/ gap from internal sides

  xpos = x+5; ypos = y+5;
  for (order = 0; order <= 1; order++) { // preview actions then live actions
    // cut before the face falls out
    if (order == (PREVIEW&1)) {
      hole_mm_f(3.0, D-margin-margin+2.0, /* at */ xpos + margin + ((W-margin-margin)/3.0)-1.5, ypos + margin - 1.0);
      hole_mm_f(3.0, D-margin-margin+2.0, /* at */ xpos + margin + ((W-margin-margin)/2.0)-1.5, ypos + margin - 1.0);
      hole_mm_f(3.0, D-margin-margin+2.0, /* at */ xpos + margin + (2.0 * (W-margin-margin) / 3.0)-1.5, ypos + margin - 1.0);

      hole_mm_f(W-margin-margin, D-margin-margin, /* at */ xpos + margin, ypos + margin);
      toothcomb(xpos + W/2.0, ypos, steps+1, 2.0, 2);
      toothcomb(xpos + W/2.0, ypos+D-2.0, steps+1, 2.0, 2);
    }
    if (order == 0) {
      face(W, D, xpos, ypos);
      hole_mm_f(3.0, D-margin-margin+2.0-0.6, /* at */ xpos + W + wood, ypos + margin - 1.0);
    }
  }
}

void ruler(int x, int y) {
  svg_line(psvg, RED, /* width */ mm_f(linewidth_f), /* from */ mm(x), mm(y), /* to */ mm(x+50), mm(y));
  svg_line(psvg, RED, /* width */ mm_f(linewidth_f), /* from */ mm(x), mm(y), /* to */ mm(x), mm(y-5));
  svg_line(psvg, RED, /* width */ mm_f(linewidth_f), /* from */ mm(x+50), mm(y), /* to */ mm(x+50), mm(y-5));
  svg_text(psvg, /* at */ mm(x+10), mm(y-1),
           "sans-serif",
           mm(3),
           /* fill colour */ BLACK, /* stroke colour */ BLACK, "5cm line", "");

}

void tray(int x, int y) {
  // base(x,y); skip base for threading test...
  base(x,y);
  struts(x, y + D + wood);
}

int main(void) {
  stage_width = 3600.0-5.0; stage_height = 2400.0-5.0;

  for (PREVIEW = 1; PREVIEW >= -1; PREVIEW--) { // do preview first to get size
    linewidth_f = PREVIEW ? VISIBLE_LINE_f : HAIRLINE_f;
    fill = PREVIEW ? PREVIEW_FILL : LIVE_FILL;

    psvg = svg_create(mm_f(stage_width+5), mm_f(stage_height+5)); // size of laser cutter bed
    stage_width = 0.0; stage_height = 0.0;

    if (psvg == NULL) {
      puts("psvg is NULL");
      exit(EXIT_FAILURE);
    } else {
      if (PREVIEW) svg_fill(psvg, BACKGROUND);
    }

    tray(0,0);

    if (PREVIEW) svg_text(psvg,
             /* at */ mm(2), mm(4),
             "sans-serif", mm(3),
             /* fill colour */ COMMENT_TEXT, /* stroke colour */ COMMENT_TEXT,
			  "THIS IS A PREVIEW-OPTIMISED VERSION.  CUT THE OTHER FILE.", "");

    svg_finalize(psvg);
    svg_save(psvg, PREVIEW ? "base-preview.svg" : "base.svg");
    svg_free(psvg);
  }

  exit(EXIT_SUCCESS);
  return EXIT_FAILURE;
}