#usage "<b>Snap objects in schematic to default grid</b>\n"
       "<p>"
       "Snaps symbols, junctions, nets and busses to the "
       "given grid (default: 0.1 inch)."
       "<p>"
       "<author>Author: support@cadsoft.de</author>"

// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED

//   English Description
string eHelp = "<qt>This EAGLE User Language Program can be used to "
      "snap the symbols, the nets, and the busses of the current "
      "schematic to default grid (0.1 Inch).<p>"
      "The first run snaps all the devices and all the net lines "
      "that are connected to a pin to default grid. "
      "The second run snaps the rest of the net lines that are "
      "not tied to a pin directly (like bends and junctions)."
      "This avoids that elements will be snapped more than once.<p>"
      " 1. with the device<p>"
      " 2. with the net lines<p>"
      " The end of the net tied to the pin can not be moved."
      " Thus EAGLE would only move the free end of a net segment"
      " again."
      "The coordinates will be stored in an array and searched for"
      "multiple occurences.<p>"
      "If devices do not snap to default grid, they are defined"
      "offgrid in the libary (symbol) editor. In this case please"
      "correct your library definition.<p>"
      "If it happens that net lines do not snap, they might be"
      "connected to a pin (as mentioned above). Or there are"
      "superimposed nets at this point. Use the SHOW command to"
      "check this closely<hr></qt>";

// *  ----------------------------------------------------------

//  Deutsche Anleitung:
string dHelp = "<qt>Dieses EAGLE ULP verschiebt Symbole, "
      "Netz-Linien und Bus-Linien im aktuellen Schaltplan auf "
      "das Default-Grid (0.1 Inch).<p>"
      "Der erste Durchlauf snapt die Devices (Symbole). "
      "Dabei werden auch alle an Pins angeschlossenen Netzsegmente "
      "automatisch gesnapt. Der zweite Durchlauf snapt nur "
      "noch die Knickpunkte, Abzweigungen und Kreuzungen der "
      "Netze. Andernfalls w&uuml;rden die Netz-Segmente durch die "
      "Mehrfachinformation mehrmals verschoben werden.<p>"
      " 1. mit dem Device.<p>"
      " 2. mit dem Netz-Segment.<p>"
      " Das Segment-Ende, das an einem Pin kontaktiert ist, "
      " kann nicht verschoben werden. So w&uuml;rde EAGLE nur das "
      " freie Segment-Ende ein weiteres Mal verschieben. "
      "Die Koordinaten werden zuerst in einer Tabelle gesammelt "
      "und nach mehrfachem Vorkommen durchsucht."
      "<p>"
      "Sollten Devices nicht auf das Default Grid gesnapt werden, "
      "sind sie in der Library (Symbol) ausserhalb des Grids "
      "definiert worden.<p>"
      "Sollten Netz-Elemente nicht verschoben werden, "
      "sind sie - wie oben erwaehnt - an einem Pin angeschlossen, "
      "oder an dieser Koordinate doppelt vorhanden. "
      "In diesem Fall ueberpruefen Sie mit SHOW, ob es sich um "
      "das gleiche Netz handelt.<hr></qt>";


string Version = "1.0.4";

int Grid = strtol(argv[2]); // default 100 Mil

int pass = strtol(argv[3]) + 1;

real snap(int n)  // returns next grid point
{
  return round(u2mil(n) / Grid) * Grid;
}

void setgridmil (void)
{
  printf("GRID mil finest;\n");
}

real onGrid(int g)
{
 return ((u2mil(g)/Grid) - round(u2mil(g)/Grid)) * Grid;

}

void move_w(int x, int y)
{
  printf("MOVE (%.4f %.4f)\n", u2mil(x), u2mil(y));
  printf("(%.4f %.4f);\n", snap(x), snap(y));
}

// main
if (!schematic) {
   dlgMessageBox("This program can only work in the schematic editor.", "OK");
   exit (0);
   }

if (Grid == 0) {
   Grid = 100;
   int Result = dlgDialog("SNAP Elements on Grid") {
      dlgHBoxLayout {
         dlgLabel("&Grid");
         dlgIntEdit(Grid);
         dlgLabel("mil");
         }
      dlgHBoxLayout {
         dlgStretch(0);
         dlgPushButton("&Help") dlgMessageBox(eHelp, "OK");
         dlgStretch(1);
         dlgPushButton("H&ilfe") dlgMessageBox(dHelp, "OK");
         dlgStretch(0);
         }
      dlgHBoxLayout {
         dlgStretch(0);
         dlgPushButton("+&OK") dlgAccept();
         dlgStretch(1);
         dlgPushButton("-&Cancel") dlgReject();
         dlgStretch(0);
         }
      };
   if (!Result) exit(-1);
   }

schematic(S) {
  string fileName;
  fileName = filesetext(S.name, "_snap.scr");

  int shift = 0;

  output(fileName) {
  printf("# This file is generated by %s %s, exported from;\n", filename(argv[0]), Version);
  printf("# %s at %s;\n", S.name, t2string(time()));
  printf("# %s;\n\n", EAGLE_SIGNATURE);
  setgridmil ();  // *** switch grid to finest
  int snapshift = 0;

  S.sheets(SH) {
    printf("EDIT .S%d;\n", SH.number);
    printf("Text '#Symbol snap\nDISPLAY NONE 94 -95 -96;\n");
    SH.parts(PA) {
      PA.instances(S) {

        // *** check Grid
        real xoff = onGrid(S.x);
        real yoff = onGrid(S.y);

        if (xoff || yoff) {
          move_w(S.x,S.y);
          snapshift =1 ;  // flag for non Wire-Snap
          shift = 1;
          }
        }
      }

    printf("Text '#Junction snap\nDISPLAY NONE 91;\n");
    SH.nets(N) {
      N.segments(SEG) {
        SEG.junctions(J) {
           real xoff = onGrid(J.x);
           real yoff = onGrid(J.y);
           if (xoff || yoff) {
              move_w(J.x,J.y);
              snapshift =1 ;  // flag for non Wire-Snap
              shift = 1;
              }
           }
        }
      }

  // * zuerst die Devices und Junctions verschieben    *
  // * damit werden alle Wire die an Pins angeschlossen *
  // * sind, auch gleich mitverschoben.                *
  // * Jeder weitere Durchlauf verschieb dann nur noch *
  // * die Netzsegmente die immer noch nicht auf dem   *
  // * Default-Grid liegen *

  if (snapshift == 0) {
    // *** check Net-Wire on Grid
    // 2. snap all Wires
    printf("Text '#Net snap\nDISPLAY NONE 91;\n");
    SH.nets(N) {
      real x[], y[];
      int countw = 0;
      N.segments(SEG) {
        SEG.wires(W) {
           int notfoundx = 1;
           int notfoundy = 1;
           int cnt = 0;
                          // * alle mehrfach vorhandenen Koordinaten ausfiltern
                          // * mehrere Segmentverbindungen muessen als
                          // * eine Koordinate behandelt werden,
                          // * um ein mehrfaches verschieben zu vermeiden
           for (cnt = 0; cnt < countw; cnt ++) {
             if (W.x1 == x[cnt] && W.y1 == y[cnt] ) {  // 1. Segmentende
                notfoundx = 0;
                break;
                }
             }
           for (cnt = 0; cnt < countw; cnt ++) {
             if (W.x2 == x[cnt] && W.y2 == y[cnt] ) {  // 2. Segmentende
                notfoundy = 0;
                break;
                }
             }
           if (notfoundx) {
             x[countw] =W.x1;
             y[countw]= W.y1;
             countw++;
             real x1off = onGrid(W.x1);
             real y1off = onGrid(W.y1);

             if (x1off || y1off) {
               move_w(W.x1,W.y1);
               shift = 1;
               }
             }
           if (notfoundy) {
             x[countw] =W.x2;
             y[countw]= W.y2;
             countw++;
             real x2off = onGrid(W.x2);
             real y2off = onGrid(W.y2);

             if (x2off || y2off) {
               move_w(W.x2,W.y2);
               shift = 1;
               }
             }
           }
        }
      }

    // 2. snap all busses
    printf("Text '#Bus snap\nDISPLAY NONE 92;\n");
    SH.busses(B) {
      real x[], y[];
      int countw = 0;
      B.segments(SEG) {
        SEG.wires(W) {
           int notfoundx = 1;
           int notfoundy = 1;
           int cnt = 0;
                          // * alle mehrfach vorhandenen Koordinaten ausfiltern
                          // * mehrere Segmentverbindungen muessen als
                          // * eine Koordinate behandelt werden,
                          // * um ein mehrfaches verschieben zu vermeiden
           for (cnt = 0; cnt < countw; cnt ++) {
             if (W.x1 == x[cnt] && W.y1 == y[cnt] ) {  // 1. Segmentende
                notfoundx = 0;
                break;
                }
             }
           for (cnt = 0; cnt < countw; cnt ++) {
             if (W.x2 == x[cnt] && W.y2 == y[cnt] ) {  // 2. Segmentende
                notfoundy = 0;
                break;
                }
             }
           if (notfoundx) {
             x[countw] =W.x1;
             y[countw]= W.y1;
             countw++;
             real x1off = onGrid(W.x1);
             real y1off = onGrid(W.y1);

             if (x1off || y1off) {
               move_w(W.x1,W.y1);
               shift = 1;
               }
             }
           if (notfoundy) {
             x[countw] =W.x2;
             y[countw]= W.y2;
             countw++;
             real x2off = onGrid(W.x2);
             real y2off = onGrid(W.y2);

             if (x2off || y2off) {
               move_w(W.x2,W.y2);
               shift = 1;
               }
             }
           }
        }
      }
    }
  }
  printf("GRID INCH;\n");
  printf("GRID 0.1;\n");
  printf("DISPLAY 91 92 94 95 96 -93;\n");
  }
 string h = "";
 if (shift) {
    sprintf(h, "run '%s' '%d' '%d' '%d';\n", argv[0], shift, Grid, pass);

    if (pass == 4) {
       sprintf(h, "# nach 3 Durchlaeufen muessen alle Elemente gesnapt sein,\n"
                  "# ueberpruefen Sie den Schaltplan auf doppelt vorkommende,\n"
                  "# oder sich überlappende Net-Wires, bzw. Junctions\n"
                  "# in der untenstehenden Liste.\n");
       string text;
       int nChars = fileread(text, fileName);

       dlgDialog(fileName) {
          dlgTextView(h + text);
          dlgHBoxLayout {
             dlgPushButton("&OK") dlgAccept();
             dlgSpacing(300);
             }
          exit (-1);
          };
       }

      exit ("script '" + fileName + "';" + h);  // do it
   }
 int Result =(dlgMessageBox("<qt><b>Nothing to do</b><p>" + S.name , "OK"));
 if (Result) exit(0);

}
