#usage "en: <qt><nobr><b>Calculate the wire length from pad to pad</b></nobr>"
       "<p>"
       "Example:<br>"
       "<nobr>RUN lpp Elementname Padname Elementname Padname [Elementname Padname Elementname Padname ...]</nobr><br> "
       "RUN lpp /G<p>"
       "You always have to specify a pair of Elementname + Padname.<br>"
       "The ULP now calculates the length of the wire connection between the given pads. "
       "So it is possible to measure the lenght of the trace between two contacts. "
       "Other PADs/SMDs which are on the way are not taken into consideration.<br> "
       "Option:<b>/G</b><br>"
       "The ULP can be used with option /G, in case you defined a group in the layout before. "
       "For each signal of the group the ULP searches for a starting point and the PAD/SMD next to it. "
       "<br>"
       "Micro, blind and buried vias are supported."
       "<p>"
       "<b>Limitations:<br>"
       "There shouldn't be several signal branches and vias at the same location, "
       "otherwise it may happen that the wrong branch will be calculated!</b><p> "
       "Length information of traces that have such a branching point with vias as well as "
       "non-continuous traces will be especially marked.<p>"
       "For displaying possible additional information double-click one of the list entries  "
       "and choose (one of) the available parameter(s) in the menu. "
       "<br>"
       "WIREs of an arbitrary PAD/SMD shape within a package will be ignored and might prompt a "
       "message that tells you that the pad is not routed! "
       "<p>"
       "<p>"
       "<author>Autor: info@az-cad.de</author>"
       ,
       "de: <qt><nobr><b>Berechnet die Länge von Leiterbahnen zwischen zwei PADs.</b></nobr>"
       "<p>"
       "Beispiel:<br>"
       "<nobr>RUN lpp Elementname Padname Elementname Padname [Elementname Padname Elementname Padname ...]</nobr><br>"
       "RUN lpp /G<p>"
       "Es muss immer ein Paar von Elementname + Padname angegeben werden.<br>"
       "Es wird nur die Länge der Leitung zwischen den beiden angegebenen Pads berechnet. "
       "Somit ist es möglich eine beliebige Strecke zwischen zwei Kontaktpunkten zu messen. "
       "Dazwischen liegende PADs/SMDs werden nicht beachtet.<br>"
       "Option:<b>/G</b><br>"
       "Man kann vorher auch eine GROUP definieren und dann mit der Option <b>/G</b> starten.<br>"
       "Dabei wird von jedem Signal in der Gruppe eine Anfangskoordinate benutzt und bis zum nächsten PAD/SMD gesucht."
       "<br>"
       "Micro-, Blind- und Buried-VIAs werden unterstützt."
       "<p>"
       "<b>Einschränkungen:<br>"
       "Es dürfen keine Mehrfachabzweigungen und VIAs an derselben Koordinate vorkommen, "
       "da sonst die falsche Abzweigung benutzt werden kann!</b><p>"
       "Längenangaben, bei denen innerhalb der Strecke Mehrfachabzweigung und zugleich VIAs vorkommen sowie "
       "nicht durchgängig geroutete Leitungen, werden enstprechend markiert.<p>"
       "Um mögliche weitere Details anzeigen zu lassen, doppelklicken Sie auf eine Zeile der Liste "
       "und wählen Sie im sich öffnenden Menü die entsprechenden (Einzel-) Parameter."
       "<br>"
       "Die WIREs eines erweiterten PAD/SMD innerhalb eines Package werden nicht berücksichtigt und führen "
       "u.U. zu der Meldung, dass das Pad nicht geroutet ist!"
       "<p>"
       "<p>"
       "<author>Autor: info@az-cad.de</author>"


string Version = "1.0.6"; // 2013-01-03 info@az-cad.de
                          // 2013-01-11
                          // 2013-01-13 Option /G
                          // 2013-03-02 Pad am Wire-Ende suchen berichtigt
                          //            Signal darf auch aus nur einem Wire-Segement bestehen bei Option /G
                          // 2013-04-16 usage und diverse Meldungen in Englisch

#require 6.0400;

string Option = "";
enum             { null, TypeW, TypeS,  TypeP, TypeV };
string Typ[]   = { "null", "Wire", "Smd ", "Pad ", "Via " };

int    wx1[];   // X coordinate
int    wy1[];   // Y coordinate
int    wx2[];   // X coordinate
int    wy2[];   // Y coordinate
int    wl[];    // Layer
int    ws[];    // Start Layer for Via & Pad, Smd and Wire
int    we[];    // End   Layer for Via & Pad, Smd and Wire == Start Layer
int    wt[];    // Type
real   wlen[];  // Länge des Wire in mm
real   LengthPadPad;
real   LengthDif[];
string wirelen[];  // Liste der berechneten Längen der Wiresegemente von Pad zu Pad
int    wused[];    // Zähler ob benutzt und zugleich die gefundene Reihenfolge
                   // ein minus Wert signalisiert eine Via
string wpart[];
string wpad[];
int    Ucnt = 1;
int    Ncoord = 0;     // Anzahl der Koordinaten (WIRE/VIA/SMD/PAD)
int    K2 = 0;         // Index auf die aktuell bearbeitete Wire-Koordiante x1y1 x2y2
int    NumSEcoord = 0; // Anzahl aller Start / End-Koordinaten
int    ErrorCoord = 0; // Zeiger auf die Koordinate, an der es klemmt.

string ElementName1;
string PadName1;
string ElementName2;
string padname2;
string StartElement;   // für die Option /G 2013-02-25
string StartPad;
string EndElement;
string EndPad;
string FoundElement;
string FoundPad;

int    StartPadX, StartPadY, EndPadX, EndPadY;
int    vs[]; // Layerstacktiefe Anfang | VIAs nie als Start benutzen, sondern die Startbedingung
int    ve[]; // Layerstacktiefe Ende   | der Wire prüfen, ob er duch die Via angebunden ist.

int    LayerStart1, LayerStart2, LayerEnd1, LayerEnd2;
int    LastX, LastY, LastK2, LastI;  // da es mehr als eine End-Koordinate geben kann,
                                     // muß immer die vorhergehende Koordinate überprüft werden.
string Header = "diff. mm\t\diff. %\tMM\tSignal\tPart\tPad\tCoord\tPart\tPad\tCoord\tComment";

int    Start1X, Start1Y, End2X, End2Y;
int    StartI, EndI, Start2I, End2I; // zum debuggen

string WireCoordText;

// ### ---------------- functions ------------------- ###
void checkshow(string line) {
  string s[];
  int cnt = strsplit(s, line, '\t');
  string list[];
  string viewheadline;
  if (language() == "de") {
    sprintf(list[0], "mm diff\t%s\tHighlighted das Signal", s[0]);
    sprintf(list[1], "%c diff\t%s\tHighlighted das Signal", '%', s[1]);
    sprintf(list[2], "Länge\t%s\tZoomt die Pad-Koordinaten und highlighted das Signal", s[2]);
    sprintf(list[3], "Signal\t%s\tHighlighted das Signal mit einer Box inkl. Strahl von links oben", s[3]);
    sprintf(list[4], "Element 1\t%s\tHighlighted das Element mit einer Box inkl. Strahl von links oben", s[4]);
    sprintf(list[5], "Pad 1\t%s\tZoomt den Pad in die Mitte und highlighted das Element", s[5]);
    sprintf(list[6], "Start-Koordinate\t%s\tZoomt die Koordinate in die Mitte und highlighted das Signal", s[6]);
    sprintf(list[7], "Element 2\t%s\tHighlighted das Element mit einer Box inkl. Strahl von links oben", s[7]);
    sprintf(list[8], "Pad 2\t%s\tZoomt den Pad in die Mitte und highlighted das Element", s[8]);
    sprintf(list[9], "End-Koordinate\t%s\tZoomt die Koordinate in die Mitte und highlighted das Signal", s[9]);
    viewheadline = "Zeige\tDetail\tBereich";
  }
  else {
    sprintf(list[0], "mm diff\t%s\thighlight the signal", s[0]);
    sprintf(list[1], "%c diff\t%s\thighlight the signal", '%', s[1]);
    sprintf(list[2], "Length\t%s\tzoom in at pad coordinates and highlight the signal", s[2]);
    sprintf(list[3], "Signal\t%s\thighlight the signal and draw box incl. pointer from upper left corner", s[3]);
    sprintf(list[4], "Element 1\t%s\thighlight the element and draw a box incl. a pointer from upper left corner", s[4]);
    sprintf(list[5], "Pad 1\t%s\tzoom and center pad and highlight element", s[5]);
    sprintf(list[6], "Start coordinate\t%s\tzoom in and center at this coordinate and highlight the signal", s[6]);
    sprintf(list[7], "Element 2\t%s\thighlight the element and draw a box with a pointer from upper left corner", s[7]);
    sprintf(list[8], "Pad 2\t%s\tzoom in and center pad and higlight the element", s[8]);
    sprintf(list[9], "End coordinate\t%s\tzoom in and center at the coordinate and highlight the signal", s[9]);
    viewheadline = "Show\tDetail\tArea";
  }
  int sel;
  int srt;
  dlgDialog("List Detail") {
    dlgHBoxLayout {
      dlgVBoxLayout dlgSpacing(220);
      dlgListView(viewheadline, list, sel, srt) {
        switch(sel) {
          case 0 : exit("SHOW "+s[3]);
          case 1 : exit("SHOW "+s[3]);
          case 2 : exit("WIN "+s[6]+" "+s[9]+";SHOW @ "+s[3]);
          case 3 : exit("SHOW @ "+s[sel]);
          case 4 : exit("SHOW @ "+s[sel]);
          case 5 : exit("WIN "+s[6]+";"+"SHOW "+s[4]);
          case 6 : exit("WIN "+s[sel]+";"+"SHOW "+s[sel]);
          case 7 : exit("SHOW @ "+s[sel]);
          case 8 : exit("WIN "+s[9]+";"+"SHOW "+s[7]);
          case 9 : exit("WIN "+s[sel]+";"+"SHOW "+s[sel]);
          default : ;
        }
      }
    }
    dlgPushButton("+OK") dlgAccept();
  };
}

// *** different length to shortesd signals in percent ***
string percent( real length, real length100) {
  string s;
  if (length100) sprintf(s, "%.3f", (length - length100) / (length100 / 100));
  else s = "--";
  return s;
}

// save data
void save(string fname, string option) {
  output(filesetext(fname, option+".lpp"), "wtD") {
    int n;
    printf("#%s : Option:%s\n%s\n", filename(argv[0]), option, Header);
    do {
      printf("%s\n", wirelen[n]);
    } while (wirelen[n++]);
  }
  exit(0);
}

real ArcLength(real ang1, real ang2, real radius) {
  return radius  * 2 * PI / 360 * (ang2 - ang1);
}

real WireLength(real x1, real x2, real y1, real y2) {
  return sqrt((pow(x2 - x1, 2) + pow(y2 - y1, 2)) );
}

real addlength(UL_WIRE W) {
  if (W.curve) {
    return ArcLength(W.arc.angle1, W.arc.angle2, u2mm(W.arc.radius));
  }
  else {
    return WireLength(u2mm(W.x2), u2mm(W.x1), u2mm(W.y2), u2mm(W.y1));
  }
}

// collect coordinate
void add(int x1, int y1, int x2, int y2, int lay, int l_start, int l_end, int type, real wirelength) {
  if (lay == 19) return; // keine Luftlinien benutzen 2013-02-27
  wx1[Ncoord] = x1;
  wy1[Ncoord] = y1;
  wx2[Ncoord] = x2;
  wy2[Ncoord] = y2;
  wl[Ncoord] = lay;
  ws[Ncoord] = l_start;
  we[Ncoord] = l_end;
  wt[Ncoord] = type;
  wlen[Ncoord] = wirelength;
  wused[Ncoord] = 0; // preset
  Ncoord++;
  return;
}

// get and check signalname on pads
string get_check_signal(string elementname1, string PadName1, string elementname2, string padname2, int cnt) {
  string found1 = "", found2 = "";
  board(B) {
    B.elements(E) {
      if (E.name == elementname1) {
        E.package.contacts(C) {
          if (C.name == PadName1) {
            found1 = C.signal;
            StartPadX = C.x;
            StartPadY = C.y;
            if (C.smd) {
              LayerStart1 = C.smd.layer;
              LayerStart2 = C.smd.layer;
            }
            else if (C.pad) {
              LayerStart1 = 1;
              LayerStart2 = 16;
            }
            break;
          }
        }
        break;
      }
    }
    B.elements(E) {
      if (E.name == elementname2) {
        E.package.contacts(C) {
          if (C.name == padname2) {
            found2 = C.signal;
            EndPadX = C.x;
            EndPadY = C.y;
            if (C.smd) {
              LayerEnd1 = C.smd.layer;
              LayerEnd2 = C.smd.layer;
            }
            else if (C.pad) {
              LayerEnd1 = 1;
              LayerEnd2 = 16;
            }
          }
        }
        break;
      }
    }
  }
  if (!found1 || !found2 || found1 != found2) {
    string err;
    if (!found1) {
        sprintf(err, "!Pad %s of element %s not found!", PadName1, ElementName1);
    }
    if (!found2) {
        sprintf(err, "!Pad %s of element %s not found!", padname2, ElementName2);
    }
    if (found1 != found2) {
      string fe1 = found1, fe2 = found2;
      if (!fe1) fe1 = "~no signal~";
      if (!fe2) fe2 = "~no signal~";
      sprintf(err, "!Different Signal #%d %s [%s %s] : %s [%s %s]", cnt, fe1, ElementName1, PadName1, fe2, ElementName2, padname2);
    }
    dlgMessageBox(err + "\nQuit with SHOW command!", "OK");
    exit("SHOW "+found1+" "+found2);
  }
  return found1;
}

// suche die weiterführende Koordinate auf dem selben Layer,
// wenn es nicht mehr weiter geht, suche nach einer Via,
// und dann wieder weiter.
int getnextcoord(int i, int k) {
  int x, y, l;
  l = wl[i]; // der Layer des Wire
  if (k == 2) { // wenn 2 suche nach Koordinate 1
    x = wx1[i];
    y = wy1[i];
  }
  else {        // wenn 1 suche nach Koordinate 2
    x = wx2[i];
    y = wy2[i];
  }

  for (int n=0; n<Ncoord; n++) {
    if (n != i && wt[n] == TypeW && l == wl[n]) { // nur in Type WIRE suchen und gleichen Layer
      if (x == wx1[n] && y == wy1[n]) {
        K2 = 1 ;           // gibt die gefundene Seite zurück
        wused[n] = Ucnt++; // zum debuggen
        return n;
      }
      if (x == wx2[n] && y == wy2[n]) {
        K2 = 2 ; // gibt die gefundene Seite zurück
        wused[n] = Ucnt++; // zum debuggen
        return n;
      }
    }
  }
  // nichts gefunden, dann suche nach Via für einen Layerwechsel
  int via = -1;
  for (n = 0; n<Ncoord; n++) {
    if (n != i && wt[n] == TypeV) { // nur in Type VIA suchen
      if (x == wx1[n] && y == wy1[n]) {
        via = n;
        wused[n] = Ucnt*-1; // zum debuggen
        break;
      }
    }
  }
  if (ws[n] <= LayerEnd1 && we[n] >= LayerEnd2) {
    if  (x == EndPadX && y == EndPadY) {
      K2 = 0;
      return n; // Ende durch Via erreicht
    }
  }
  if (via >= 0) {  // ist eine Via an diesem "Ende", dann suche in einem anderen Layer weiter.
    for (int n = 0; n <Ncoord; n++) {
      if (n != i && wt[n] == TypeW && wl[n] != l) { // nur in Type WIRE suchen und bei anderem Layer
        if (x == wx1[n] && y == wy1[n]) {
          K2 = 1; // gibt die gefundene Seite zurück
          wused[n] = Ucnt++;
          return n;
        }
        if (x == wx2[n]&& y == wy2[n]) {
          K2 = 2;  // gibt die gefundene Seite zurück
          wused[n] = Ucnt++;
          return n;
        }
      }
    }
  }
  ErrorCoord = i;
  return -1;
}

/* suche nach Endkoordinate für die Pad zu Pad Option*/
int getendpadpad(int start, int sk, string sigName) { // die Startseite k wird übergeben
  int cntdebug = 0; // zum debuggen
  Ucnt = 001;       // zum debuggen
  wused[start] = Ucnt++; // zum debuggen
  string debug;     // zum debuggen

  int x, y;
  int i = start;
  K2 = sk;
  int endeerreicht = -1;
  do { /********** suche durch die Liste bis zum End-Pad **********/
    LengthPadPad += wlen[i];
    LastI = i;
    LastK2 = K2;
    if (K2 == 2) {  // suche nach der anderen Seite von 2
      LastX = wx1[i];
      LastY = wy1[i];
      x = wx1[i];
      y = wy1[i];
    }
    else {  // suche nach der anderen Seite von 1
      LastX = wx2[i];
      LastY = wy2[i];
      x = wx2[i];
      y = wy2[i];
    }



    // gleich mal prüfen ob das Ende schon erreicht ist
    if (ws[i] <= LayerEnd1 && we[i] >= LayerEnd2) { // passt der Layer
      if (K2 == 1) { // K2 == 1
        if (EndPadX == wx2[i] &&  EndPadY == wy2[i]) {  // suche nach der anderen Seite wie k
          return i;
        }
      }
      else { // dann K2 == 2
        if (EndPadX == wx1[i] &&  EndPadY == wy1[i]) {  // suche nach der anderen Seite von k
          return i;
        }
      }
    }// Ende von if gleich mal prüfen ob das Ende schon erreicht ist
    /* durchsuche die Liste auf die nachfolgende Koordinate */

    /****** VIAs sind in der Suche eingeschlossen, werden also übersprungen. ******/
    i = getnextcoord(i, K2);  // K2 ist Global! Eine Funktion kann nur einen Return-Wert zurückgeben.
    /****** VIAs sind in der Suche eingeschlossen, werden also übersprungen. ******/
    if (i < 0) return i; /*** hier ist es zu Ende, wenn keine weiterführende Koordinate gefunden wurde ***/


    if (K2 == 1) { // K2 == 1
      if (EndPadX == wx2[i] &&  EndPadY == wy2[i]) {  // suche nach der anderen Seite wie k
        LengthPadPad += wlen[i];  // 2013-03-02
        return i;
      }
    }
    else { // dann K2 == 2
      if (EndPadX == wx1[i] &&  EndPadY == wy1[i]) {  // suche nach der anderen Seite von k
        LengthPadPad += wlen[i]; // 2013-03-02
        return i;
      }
    }
    cntdebug++;
  } while(i >= 0);  /********** suche durch die Liste bis zum End-Pad **********/

  // index zum zuletzt gefundenen Wire der auf den PAD/SMD zeigt
  return i;  // - negative Zahl heisst das Ende nicht gefunden!
}

// überprüfe ob der Wire über eine Via an den SMD (Pad) angebuden ist.
int viaandindung(int l, int cntv) {
  int fehler = 0;
  string v;
  for (int cnt = 0; cnt < cntv; cnt++) {
    if (l >= vs[cnt] && l <= ve[cnt]) return 1;  // evtl. noch die Stacktiefe auf die Länge addieren?
    sprintf(v, "WIRE layer %d : VIA Layer %d-%d", l, vs[cnt], ve[cnt]);
  }
  dlgMessageBox(v);
  return 0;
}

/*** Hauptschleife der Option [Elementname Pad Elementname Padname] ***/
// ermittle Start-Koordinaten in den Wire-Koordinaten für dieses Signal
real checkcoordpadpad(string sigName) {
  // 2013-01-09 info@az-cad.de
  NumSEcoord = 0;
  int cntstart = 0;
  int cntend = 0;
  int indexstart[];
  int wk[];
  int x;
  int cnti = 0;
  int cntv = 0;
  for (x = 0; x < Ncoord; x++) {  // Suche zuerst eventuelle Start-VIAs
    if (StartPadX == wx1[x] && StartPadY == wy1[x] && (wt[x] ==  TypeV)) { // Via hat 1. Koordinate gleich wie 2. Koordinate
      vs[cntv] = ws[x];
      ve[cntv] = we[x];
      cntv++;
    }
  }

  for (x = 0; x < Ncoord; x++) {
    // Eine VIA nie als Startbedingung benutzen, sondern gleich die entsprechenden Wire suchen,
    // die an der Via angebunden sind.
    if (StartPadX == wx1[x] && StartPadY == wy1[x] && wt[x] ==  TypeW) { // Die erste Seite des Wire
      if (LayerStart1 == ws[x] || viaandindung(ws[x], cntv)) {
        indexstart[cntstart] = x;
        wk[cntstart] = 1;  // die Startseite
        LayerStart1 = ws[x];
        LayerStart2 = we[x];
        cntstart++;
        NumSEcoord++;
      }
    }
    if (StartPadX == wx2[x] && StartPadY == wy2[x] &&  wt[x] ==  TypeW) { // die zweite Seite des Wire
      if (LayerStart1 == ws[x] || viaandindung(ws[x], cntv)) {
        indexstart[cntstart] = x;
        wk[cntstart] = 2;  // die Startseite
        LayerStart1 = ws[x];
        LayerStart2 = we[x];
        cntstart++;
        NumSEcoord++;
      }
    }
    // prüfe auch ob die Enden geroutet sind
    if (EndPadX == wx1[x] && EndPadY == wy1[x] && (wt[x] ==  TypeW || wt[x] == TypeV)) {
      cntend++;
    }
    if (EndPadX == wx2[x] && EndPadY == wy2[x] &&  (wt[x] ==  TypeW || wt[x] == TypeV)) {
      cntend++;
    }
  }

  string errorinfo = "";
  if (!cntstart) {
    if (language() == "de") sprintf(errorinfo, "%s PAD %s nicht geroutet!", ElementName1, PadName1);
    else  sprintf(errorinfo, "%s PAD %s not routed!", ElementName1, PadName1);
    dlgMessageBox(errorinfo);
    sprintf(errorinfo, "WIN (%.fmm %.fmm);SHOW (%.fmm %.fmm)", u2mm(StartPadX), u2mm(StartPadY), u2mm(StartPadX), u2mm(StartPadY));
    exit(errorinfo);
  }
  if (!cntend) {
    if (language() == "de") sprintf(errorinfo, "%s PAD %s nicht geroutet!", ElementName2, padname2);
     sprintf(errorinfo, "%s PAD %s not routed!", ElementName2, padname2);
    dlgMessageBox(errorinfo);
    sprintf(errorinfo, "WIN (%.fmm %.fmm);SHOW (%.fmm %.fmm)", u2mm(EndPadX), u2mm(EndPadY), u2mm(EndPadX), u2mm(EndPadY));
    exit(errorinfo);
  }

  int foundend = -1;
  int n = 0;
  if (Ncoord > 1) {
    if (NumSEcoord) {
      foundend = -1;
      // ### loop ###
      for (int s = 0; s < cntstart; s++) {  // durchsuche alle Start-Koordinaten
        LengthPadPad = 0;  // die Stelle in der Funktion Pad-Pad zum reseten der Gesamtlänge
        foundend = getendpadpad(indexstart[s], wk[s], sigName); // suche das Ende (PAD/SMD)
        if (foundend >= 0) return LengthPadPad;
      }
    }
  }

  return LengthPadPad *= -1.0; // * -1.0;  // erzeuge im Fehlerfall ein negatives Vorzeichen
  //return LengthPadPad;
}

// ******************************************************
// suche die weiterführende Koordinate auf dem selben Layer,
// wenn es nicht mehr weiter geht, suche nach einer Via,
// und dann wieder weiter.
int getnextcoordpad(int i, int k) {
  int x, y, l;
  l = wl[i]; // der Layer des Wire

  if (k == 2) { // wenn 2 suche nach Koordinate 1
    x = wx1[i];
    y = wy1[i];
  }
  else {        // wenn 1 suche nach Koordinate 2
    x = wx2[i];
    y = wy2[i];
  }

  int e;
  for (e = Ncoord-1; e; e--) {  // suche vom Ende der Liste her, die Pad/Smd stehen am Schluß
    if (wt[e] == TypeP || wt[e] == TypeS) { // in Type PAD und SMD suchen
      if (ws[e] >= ws[i] && we[e] <= we[i]) { // Trifft der Layer des Wire auf den PAD
        if (x == wx1[e] && y == wy1[e]) {
          wused[e] = Ucnt++; // zum debuggen
          LastI = e;
          return e;
        }
      }
    }
  }
  // in Type WIRE suchen und mit gleichen Layer
  for (int n=0; n<Ncoord; n++) {
    if (n != i && wt[n] == TypeW && l == wl[n]) {
      if (x == wx1[n] && y == wy1[n]) {
        K2 = 1 ;  // gibt die gefundene Seite zurück
        wused[n] = Ucnt++; // zum debuggen
        return n;
      }
      if (x == wx2[n] && y == wy2[n]) {
        K2 = 2 ; // gibt die gefundene Seite zurück
        wused[n] = Ucnt++; // zum debuggen
        return n;
      }
    }
  }

  // nichts gefunden? Dann suche nach Via für einen Layer wechsel
  int via = -1;
  for (n = 0; n<Ncoord; n++) {
    if (n != i && wt[n] == TypeV) { // nur in Type VIA suchen
      if (x == wx1[n] && y == wy1[n]) {
        via = n;
        wused[n] = Ucnt*-1; // zum debuggen
        break;
      }
    }
  }

  if (via >= 0) {  // ist eine Via an diesem "Ende", dann suche in einem anderen Layer weiter.
    // gleich mal prüfen ob die Via einen Pad/Smd erreicht?
    for (e = Ncoord-1; e; e--) {  // suche vom Ende der Liste her, die Pad/Smd stehen am Schluß
      if (wt[e] == TypeP || wt[e] == TypeS) { // in Type PAD und SMD suchen
        if (x == wx1[e] && y == wy1[e]) {
          if (ws[e] >= ws[n] && we[e] <= we[n]) { // Trifft der Layer des Via auf den PAD
            wused[e] = Ucnt++; // zum debuggen
            LastI = e;
            return e;
          }
        }
      }
    }

    for (int n = 0; n <Ncoord; n++) {
      if (n != i && wt[n] == TypeW && wl[n] != l) { // nur in Type WIRE suchen und bei anderem Layer
        if (x == wx1[n] && y == wy1[n]) {
          K2 = 1; // gibt die gefundene Seite zurück
          wused[n] = Ucnt++;
          return n;
        }
        if (x == wx2[n]&& y == wy2[n]) {
          K2 = 2;  // gibt die gefundene Seite zurück
          wused[n] = Ucnt++;
          return n;
        }
      }
    }
  }
  ErrorCoord = i;
  return -1;
}

// *********************************************************
//  Option Gruppe markiert. Suche nach Pad am Ende
int getendgrouppad(int start, int sk, string sigName) { // die Startseite k wird übergeben
  int cnt = 0;    // zum debuggen
  Ucnt = 001;     // zum debuggen
  string ncoords; // zum debuggen
  int x, y;
  int i = start;
  K2 = sk;
  int e;
  do {
    LastI = i;
    LastK2 = K2;
    if (K2 == 2) {  // suche nach der anderen Seite
      LastX = wx1[i];
      LastY = wy1[i];
      x = wx1[i];
      y = wy1[i];
    }
    else {
      LastX = wx2[i];
      LastY = wy2[i];
      x = wx2[i];
      y = wy2[i];
    }
    LengthPadPad += wlen[i];

    // gleich mal prüfen ob das Ende schon erreicht ist.
    int e;
    for (e = Ncoord-1; e; e--) {  // suche vom Ende der Liste her, die Pad/Smd stehen am Schluß
      if (wt[e] == TypeP || wt[e] == TypeS) { // in Type PAD und SMD suchen
        if (ws[e] >= ws[i] && we[e] <= we[i]) { // Trifft der Layer des Wire auf den Layer des PAD
          if (x == wx1[e] && y == wy1[e]) {
            wused[e] = Ucnt++; // zum debuggen
            LastI = e;
            return e;
          }
        }
      }
    }

    // nichts gefunden? Dann suche nach Via für einen Layer wechsel
    int via = -1;
    int n;
    for (n = 0; n<Ncoord; n++) {
      if (n != i && wt[n] == TypeV) { // nur in Type VIA suchen
        if (ws[n] >= ws[i] && we[n] <= we[i]) { // Trifft der Layer des Wire auf den Layer des PAD
          if (x == wx1[n] && y == wy1[n]) {
            via = n;
            wused[n] = Ucnt*-1; // zum debuggen
            break;
          }
        }
      }
    }

    if (via >= 0) {  // ist eine Via an diesem "Ende", dann suche in einem anderen Layer weiter.
      // gleich mal prüfen ob die Via einen Pad/Smd erreicht?
      for (e = Ncoord-1; e; e--) {  // suche vom Ende der Liste her, die Pad/Smd stehen am Schluß
        if (wt[e] == TypeP || wt[e] == TypeS) { // in Type PAD und SMD suchen
          if (x == wx1[e] && y == wy1[e]) {
            if (ws[e] >= ws[n] && we[e] <= we[n]) { // Trifft der Layer des Via auf den PAD
              wused[e] = Ucnt++; // zum debuggen
              LastI = e;
              return e;
            }
          }
        }
      }
    }
    //
    for (e = Ncoord-1; e; e--) {  // suche vom Ende des Array, die Pad/Smd stehen am Schluß
      if (x == wx1[e] && y == wy1[e] && (wt[e] == TypeP || wt[e] == TypeS)) { // ist der Type ein SMD oder PAD
        if (ws[e] <= ws[i] && we[e] >= we[i]) { // Trifft der Layer des Wire auf den PAD
          LastX = x;
          LastY = y;
          LastI = e;
          return e; // 2013-01-15
        }
      }
    }

    i = getnextcoordpad(i, K2);  // K2 ist Global! Weil eine Funktion nur einen Return-Wert zurückgeben kann.

    cnt++;
  } while(i >= 0);
  LastI = i;
  return i; // 2013-01-15 Ende nicht gefunden
}


//  Option Gruppe markiert. Suche nach dem ersten Pad/Smd in der Kette
real searchgrouppad(int n, int k, string signame) {
  LengthPadPad = 0.0; // die Einzig wahre Stelle zum reseten der Gesamtlänge des Signal in der Group-Option
  int foundend = -1;
  foundend = getendgrouppad(n, k, signame);  // die Startseite k wird übergeben

  if (foundend >= 0) {
    FoundElement = wpart[foundend];
    FoundPad = wpad[foundend];
    return LengthPadPad;
  }
  return LengthPadPad * -1.0;  // erzeuge im Fehlerfall ein negatives Vorzeichen
}


//  Option Gruppe markiert. Messe die Wire des markierten Signal
real measuringgroup(UL_WIRE W, UL_SIGNAL S, int wx, int wy) { // samme alle koordinaten und suche nach wx1 wy1
  Ncoord = 0;
  S.wires(W) { // zuerst alle Wire sammeln.
    add(W.x1, W.y1, W.x2, W.y2, W.layer, W.layer, W.layer, TypeW, addlength(W));
  }
  S.vias(V) { // dann die Vias sammeln
    add(V.x, V.y, V.x, V.y, 18, V.start, V.end, TypeV, 0);  // hier evtl. die Bohrtiefe aus den DRC-Daten berechnen 2013-01-02
  }
  S.contactrefs(C) {
    if (C.contact) { // jetzt noch die CONTACTs sammeln
      wpart[Ncoord] = C.element.name;
      wpad[Ncoord] = C.contact.name;
      if (C.contact.smd) {
        add(C.contact.x, C.contact.y, C.contact.x, C.contact.y, C.contact.smd.layer, C.contact.smd.layer, C.contact.smd.layer, TypeS, 0);
      }
      else {
        add(C.contact.pad.x, C.contact.pad.y, C.contact.pad.x, C.contact.pad.y, 17, 1, 16, TypeP, 0);
      }
    }
    else {
      if (language() == "de") dlgMessageBox("Signal '"+ S.name + "' ohne Contactrefs gefunden!\nPrüfen Sie die Konsistenz und evtl. korrupte Signale!", "OK");
      else dlgMessageBox("Found signal '"+ S.name + "' without contactrefs!\nCheck consistency and corrupted signals!", "OK");
      exit(-1);
    }
  }
  // Das Problem beim Selektieren ist, man kann mit Group keinen Wire in der Mitte selektieren,
  // sondern nur an den Enden.
  real len = 0.0;
  int cntw = 0;
  int n;
  /** Reset all global strings **/
  StartPad = "";
  FoundPad = "";
  StartElement = "";
  FoundElement = "";
  EndPad = "";
  FoundPad = "";
  EndElement = "";
  FoundElement = "";

  string h;
  for (n = 0; n < Ncoord; n++) {
    if (wx == wx1[n] && wy == wy1[n] && TypeW == wt[n]) {
      len += searchgrouppad(n, 1, S.name);
      StartPad = FoundPad;
      StartElement = FoundElement;
      Start1X = LastX;
      Start1Y = LastY;
      len += searchgrouppad(n, 2, S.name) - wlen[n]; // suche auch nach der anderen Seite
      EndPad = FoundPad;
      EndElement = FoundElement;
      End2X = LastX;
      End2Y = LastY;
      cntw++;
      n = Ncoord; // 2013-02-27 den Wire nur einmal benutzen
    }
    else {
      if (wx == wx2[n] && wy == wy2[n] && TypeW == wt[n]) {
        len += searchgrouppad(n, 2, S.name);
        StartPad = FoundPad;
        StartElement = FoundElement;
        Start1X = LastX;
        Start1Y = LastY;
        len += searchgrouppad(n, 1, S.name) - wlen[n]; // suche auch nach der anderen Seite
        EndPad = FoundPad;
        EndElement = FoundElement;
        End2X = LastX;
        End2Y = LastY;
        cntw++;
        n = Ncoord; // 2013-02-27 den Wire nur einmal benutzen
      }
    }
  }
  if (cntw > 2) {
    string h;
    if (language() == "de") sprintf(h, "!Signal %s\nStartkoordinate enthält Abzweigung!", S.name);
    else sprintf(h, "!Signal %s\nBranching on starting coordinate!", S.name);
    if (dlgMessageBox(h, "OK", "ESC") != 0) {
      string s;
      sprintf(s, "WIN (%.6fmm %.6fmm);", u2mm(wx), u2mm(wy) );
      exit(s);
    }
  }
  return len;
}

// Option Gruppe markiert, suche nach Wire im Signal die in der Gruppe enthalten sind.
int usegroup(void) {
  int l = 0;
  board(B) {
    B.signals(S) {
      if (ingroup(S)) {  // Signal ist in der Group
        int nx = 0;
        S.wires(W) {
          if (ingroup(W)) {
            real len = measuringgroup(W, S, W.x1, W.y1); // sammel alle koordinaten und suche nach wx1 wy1
            LengthDif[l] = len;
            if (StartElement == EndElement && StartPad == EndPad) {
                sprintf(wirelen[l], "%s\t%s\t?%s\t?%s\t(%.9fmm %.9fmm)\t?%s\t?%s\t(%.9fmm %.9fmm)",
                                      "*start==end*", S.name,
                                      StartElement, StartPad,
                                      u2mm(Start1X), u2mm(Start1Y),
                                      EndElement, EndPad,
                                      u2mm(End2X), u2mm(End2Y)
                       );
            }
            else if ((!StartElement && !StartPad) && EndElement && EndPad) {
                sprintf(wirelen[l], "%s\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                      "*not routed*", S.name,
                                      "???", "???",
                                      u2mm(Start1X), u2mm(Start1Y),
                                      EndElement, EndPad,
                                      u2mm(End2X), u2mm(End2Y)
                       );
            }
            else if (StartElement && StartPad && (!EndElement && !EndPad)) {
                sprintf(wirelen[l], "%s\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                      "*not routed*", S.name,
                                      StartElement, StartPad,
                                      u2mm(Start1X), u2mm(Start1Y),
                                      "???", "???",
                                      u2mm(End2X), u2mm(End2Y)
                       );
            }
            else {
              if (len < 0.0) {
                LengthDif[l] = 0.0; // 2013-02-27
                sprintf(wirelen[l], "*%s\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                      "not found", S.name,
                                      StartElement, StartPad,
                                      u2mm(Start1X), u2mm(Start1Y),
                                      EndElement, EndPad,
                                      u2mm(End2X), u2mm(End2Y)
                       );
              }
              else {
                sprintf(wirelen[l], "%.9f\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                      len, S.name,
                                      StartElement, StartPad,
                                      u2mm(Start1X), u2mm(Start1Y),
                                      EndElement, EndPad,
                                      u2mm(End2X), u2mm(End2Y)
                       );
              }
            }
            l++;
            break; // W-Schleife abbrechen, nur einen Wire (Koordinate) aus der Gruppe benutzen
          }
        }
      }
    }
    if (!l) {
      if (language() == "de") dlgMessageBox("!Keine Gruppe definiert. ", "OK");
      else  dlgMessageBox("!No group defined. ", "OK");
      exit(-4);
    }
  }
  return l;
}

// ### main ###
if (board) {
  board(B) {
    int l = 0;
    if (argc == 2 && argv[1] == "/G") { // benutze Option Group
      Option = "~group";
      l = usegroup();
    } // Ende von benutze definierte Group

    else { // benutze Option [Element Pad Element Pad]
      Option = "~pad-pad";
      int cnt = argc-1;
      if (argc > 1) {
        string h;
        if (cnt < 4) {
          if (language() == "de") sprintf(h, "!Zu wenig Parameter: %d", cnt);
          else  sprintf(h, "!Insufficient number of parameters: %d", cnt);
          dlgMessageBox(h, "OK");
          exit(-3);
        }
        else {
          if (cnt & 3 != 2) {
            if (language() == "de") sprintf(h, "!Ungenügende Anzahl von Parametern: %d", cnt);
            else sprintf(h, "!Insufficient number of parameters: %d", cnt);
            dlgMessageBox(h, "OK");
            exit(-2);
          }
        }
      }
      else {
        dlgDialog("HELP: Lenght PAD PAD") {
          dlgLabel(usage);
          dlgHBoxLayout {
            dlgStretch(1);
            dlgPushButton("+OK") dlgAccept();
            dlgStretch(1);
          }
        };
        exit(0);
      }
      l = 0;
      string sig;
      for (int n = 1; n <= cnt; n+=4) {
        ElementName1 = strupr(argv[n]);
        PadName1 = strupr(argv[n+1]);
        ElementName2 = strupr(argv[n+2]);
        padname2 = strupr(argv[n+3]);

        sig = get_check_signal(ElementName1, PadName1, ElementName2, padname2, n);
        string h;
        sprintf(h, "%s %s (%.9f_%.9f) %s %s (%.9f_%.9f)\n",
                    ElementName1, PadName1, u2mm(StartPadX), u2mm(StartPadY),
                    ElementName2, padname2, u2mm(EndPadX), u2mm(EndPadY)
               );
        WireCoordText += h;

        B.signals(S) {
          status(" check "+sig);
          if (S.name == sig) {  // Der Signalname der Pads ist gefunden.
            Ncoord = 0;
            S.wires(W) {        // zuerst alle Wire sammeln. 2013-01-09
                add(W.x1, W.y1, W.x2, W.y2, W.layer, W.layer, W.layer, TypeW, addlength(W));
            }
            S.vias(V) {         // dann die Vias sammeln
              add(V.x, V.y, V.x, V.y, 18, V.start, V.end, TypeV, 0);  // hier evtl. die Bohrtiefe aus den DRC-Daten berechnen 2013-01-02
            }
            S.contactrefs(C) {
              if (C.contact) {  // jetzt noch den CONTACT ermitteln, das sind die letzen beiden. 2013-01-09
                if (C.contact.smd) {
                  add(C.contact.x, C.contact.y, C.contact.x, C.contact.y, C.contact.smd.layer, C.contact.smd.layer, C.contact.smd.layer, TypeS, 0);
                }
                else {
                  add(C.contact.pad.x, C.contact.pad.y, C.contact.pad.x, C.contact.pad.y, 17, 1, 16, TypeP, 0);
                }
              }
            }
            real len = checkcoordpadpad(S.name); // Suche die Endkoordinaten und ermittle die Länge
            LengthDif[l] = len;
            if (len < 0.0) {
              sprintf(wirelen[l++], "%s\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                   "*not routet*", // markiere nicht geroutete Verbindung
                                   S.name,
                                   ElementName1, PadName1, u2mm(StartPadX), u2mm(StartPadY),
                                   ElementName2, padname2, u2mm(wx2[ErrorCoord]), u2mm(wy2[ErrorCoord])
                     );
            }
            else {
              sprintf(wirelen[l++], "%.9f\t%s\t%s\t%s\t(%.9fmm %.9fmm)\t%s\t%s\t(%.9fmm %.9fmm)",
                                   len, S.name,
                                   ElementName1, PadName1, u2mm(StartPadX), u2mm(StartPadY),
                                   ElementName2, padname2, u2mm(EndPadX), u2mm(EndPadY)
                     );
            }
          }
        }
      }
    }  // Ende von benutzen Element Pad Element Pad

    int index[];
    sort(l, index, LengthDif);
    real null_length = LengthDif[index[0]];
    string h;
    for (int i = 0; i < l; ++i) {
      sprintf(h, "%.6f\t%s\t",
                  LengthDif[index[i]] - null_length,
                  percent(LengthDif[index[i]], null_length)
             );
      wirelen[index[i]] = h + wirelen[index[i]];
    }

    int sel = -1;
    int srt = 0 ;
    dlgDialog("Lenght PAD PAD" + Option) {
      dlgLabel("Option:" + Option);
      dlgHBoxLayout {
        dlgVBoxLayout dlgSpacing(200);
        dlgListView(Header, wirelen, sel, srt) dlgAccept();
      }
      if (language() == "de") dlgLabel("Um Details eines Eintrag anzuzeigen, selektieren (doppelklicken) Sie eine Zeile.");
      else  dlgLabel("To show details of a list entry, select (double click) a line.");
      dlgHBoxLayout {
        dlgPushButton("+OK") dlgAccept();
        dlgPushButton("&Save") { dlgAccept(); save(B.name, Option); }
        dlgStretch(1);
        dlgLabel(Version);
      }
    };
    if (sel >= 0)  checkshow(wirelen[sel]);
    exit(0);
  }
}

else {
  if (language() == "de") dlgMessageBox("Starten Sie das ULP in einem Board", "OK");
  else dlgMessageBox("Start this ULP in a Board", "OK");
}
