#usage "<b>Export NC drill file</b>\n"
       "<p>"
       "Generates an NC drill file for the current board."
       "<p>"
       "Units: mm or inches can be chosen."
       "<p>"
       "<author>Author: Andreas Andersson, sorting added by Norman U. Baier</author>"
       /* Well, actually most of the file is nicked from the
          default drill rack file that comes with Eagle */

/*
 *  This EAGLE User Language Program generates
 *  an NC drillfile for the current board.
 *
 *  Units can be switched between mm and inch.
 */

string fileName;

string unitName[] = { "mm", "in" };
int    Unit;
int    unitPrec[] = { 2, 3 };
int    RoundFactor = pow(10, unitPrec[Unit]);
int    Result = 0;

int holesMax = 0 ;
int holesIndex[] ;
string holesArray[] ;
string holesHeaderArray[] ;

real   Sizes[];
int    currentTool = 0;
int    toolsMax = 0;
string Holes = "";

enum { unitMM, unitINCH};

int getunit (void)
{
  int Unit = unitMM;
  Result = dlgDialog("Gcode drill file") {
    dlgHBoxLayout {
       dlgStretch(0);
       dlgGroup("Select measurement system for output file") {
          dlgStretch(0);
          dlgGridLayout {
             dlgCell(1, 0)  dlgRadioButton("&Metric", Unit);
             dlgCell(2, 0)  dlgRadioButton("&Imperial", Unit);
          }
          dlgSpacing(5);
          dlgHBoxLayout {
             dlgStretch(0);
             dlgLabel("Files are generated in x.3 format, leading zero suppression.");
             dlgStretch(1);
          }
       }
       dlgStretch(0);
       dlgVBoxLayout {
          dlgHBoxLayout {
             dlgSpacing(20);
             dlgStretch(0);
             dlgPushButton("+OK") dlgAccept();
             dlgStretch(1);
          }
          dlgHBoxLayout {
             dlgSpacing(20);
             dlgStretch(0);
             dlgPushButton("-Quit") dlgReject();
             dlgStretch(1);
          }
       }
       dlgStretch(1);
    }
    dlgStretch(1);
  };
  RoundFactor = pow(10, unitPrec[Unit]);
  return Unit;
}

void AddHole(int Size, int x, int y)
{
  real r_size;
  real x_int, y_int;
  string tempstr;

  switch (Unit) {
    case unitMM:
      r_size = round(u2mm(Size) * RoundFactor) / RoundFactor;
      /* x.3 format */
      x_int = round(u2mm(x)*1000);
      y_int = round(u2mm(y)*1000);
      break;
    case unitINCH:
     r_size = round(u2inch(Size) * RoundFactor) / RoundFactor;

      /* x.3 format */
    x_int = round(u2inch(x)*1000)/1000;
    y_int = round(u2inch(y)*1000)/1000;
      break;
  }

  for (int k = toolsMax; --k >= 0; ) {
    if (Sizes[k] == r_size) {
      holesMax++ ;
      /* Add coordinates */
      sprintf(tempstr, "G82 X%.6fY%.6f \n", x_int, y_int);
      holesIndex[holesMax] = k+1 ;
      holesArray[holesMax] = tempstr;
      return;
    }
  }

  currentTool++ ;
  Sizes[toolsMax++] = r_size;
  sprintf(tempstr, "M05\nM06 T0\n(Insert T%02d)\nM03\n", currentTool);
  holesHeaderArray[toolsMax] = tempstr;

  /* Add coordinates */
  sprintf(tempstr, "G82 X%.6fY%.6f Z-0.08 F1.0 R0.1 #250\n", x_int, y_int);
  holesHeaderArray[toolsMax] += tempstr;
}

void sortHoles(){

for (int toolCounter=toolsMax+1; --toolCounter >=0; ){
  Holes += holesHeaderArray[toolCounter] ;
  for (int holeCounter=holesMax+1; --holeCounter >=0;){
    if (holesIndex[holeCounter] == toolCounter){
      Holes += holesArray[holeCounter] ;
      }
    }
  }
}

/* "Main" */
Unit = getunit();
if (Result == 0) exit(0);

board(B) {
  B.holes(H) AddHole(H.drill, H.x, H.y);
  B.signals(S) S.vias(V) AddHole(V.drill, V.x, V.y);
  B.elements(E) {
    E.package.contacts(C) {
      if (C.pad) {
         AddHole(C.pad.drill, C.pad.x, C.pad.y);
      }
    }
    E.package.holes(H) AddHole(H.drill, H.x, H.y);
  }
  sortHoles() ;

  fileName = dlgFileSave("Save Drill File", filesetext(B.name, ".drl"), "*.drl");
  if (fileName == "") exit(0);

  output(fileName) {

printf ("(This GCode generated with nc-drill_gcode.ulp program)\n");
         printf ("(running under Eagle CAD.)\n");
         printf ("(Use with #60 bits)\n");
         printf ("(X/Y/Z home should be at lower left corner of the board, with the tip homed with 0 being tip touching board)\n");

    /* Tool definitions */
    for (int i = 0; i < toolsMax; ++i) {
      printf("T%02dC%2.*f\n", i + 1, unitPrec[Unit], Sizes[i]);
    }

    /* Start of data */
    printf("%%\n");
    /* Absolute mode */
    printf("G90\n");
//printf("M03\n");

    /* Set measurement system */
    switch (Unit) {
      case unitMM:
        printf("M71\n");
        break;
      case unitINCH:
        printf("M72\n");
        break;
    }

    /* Output all the hole coordinates */
    printf("%s", Holes);
  printf("(drill hole here)\n");
  printf("do end stuff here.\n");
         printf("(Finished.  Raise cutter, turn spindle off, reset.)\n");
         printf("G0Z0.23\n");
         printf("M05\n");
         printf("G0X0Y0\n");
         printf("M30\n");

    /* End of file */
  }
}
