#usage "<b>This ULP calculates the signal length of routed tracks in the layout</b><p><nobr>\n"
       "run length [name | name* | *name | *name*]<br>"
       "run length name [name name ...]<p>"
       "EXAMPLE:<br>"
       "run length +D -D<br>"
       "run length d0 d2 d7 A*<br>"
       "run length d*<br>"
       "run length *<p>"
       "Wires in layers 1 to 16 will be added, airwires will be shown separately.<br>"
       "If net names are specified, the differences in length will be calculated in procentual values.<br>"
       "The procentual difference is based on the shortest of the specified tracks which is taken as 100%.<br>"
       "Parallel tracks and polygons are not taken into consideration.<br>"
       "Only for all signals it is possible to change the sorting of the displayed signals in the list."
       "<p>"
       "<author>Author: support@cadsoft.de</author></nobr>"

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

#require 4.1602

// **  German description: **
string hilfe = "<nobr><b>Dieses ULP berechnet die Signallängen von Leiterbahnen eines Layouts.</b><p>" +
               "run length [name | name* | *name | *name*]<br>" +
               "run length name [name name ...]<p>" +
               "Beispiel:<br>" +
               "run length +D -D<br>" +
               "run length d0 d2 d7 d6 A*<br>" +
               "run length d*<br>" +
               "run length *<p>" +
               "Die Wire in den Layern 1 bis 16 werden addiert, die Airwire werden gesondert angegeben.<br>" +
               "Prozentuale Längendifferenzen werden nur berechnet wenn Netznamen angegeben wurden.<br>" +
               "Für den prozentualen Längenunterschied wird die kürzeste Leiterbahn als 100% angesehen.<br>" +
               "Es wird keine Parallelführung bzw. Fläche (Polygon) berücksichtigt.<br>" +
               "Nur bei Anzeige aller Signale kann die Sortierung des Listfeldes beeinflußt werden.<p>" +
               "Ein Doppelklick auf einen Listeneintrag, beendet das ULP und zeigt des Singal im Board.<p>" +
               "<author>Author: support@cadsoft.de</author>"
               "</nobr>";

//  2008-09-11 Ohne Parameter wird erst die Hilfe aufgerufen. alf@cadsoft.de
//             Wird keines der gesuchten Signale gefunden,
//             wird eine entsprechnede Nachricht angezeigt.
//  2005-02-10 alf@cadsoft.de
//  2012-04-05 Doppleklick in Liste beendet das ULP mit SHOW auf den Netznamen
//  2013-01-09 Jetzt auch mit * ohne das Hilfsmenu un mit 9 Nachkommastellen


string Help = usage;
string HButton = "&Help";
string SButton = "&Save";

real f, WLtotal;
int index[];


real   Length[];
string signal_list[];
int    sig_n = 0;

real   route_length[];
real   Unroute_length[];
int    Unroute_cnt[];

int t;
numeric string data[];

string h;
string header;

data[1] = "No signals found.";

if (language() == "de") {
  data[1] = "Keines der Signale gefunden."; // 2008-09-11
  Help = hilfe;
  HButton = "&Hilfe";
  SButton = "&Sichern";
}


void help(void) {
  dlgMessageBox(Help, "Ok");
  return;
}


void show(string line) {  // 2012-04-05
  string s[];
  int n = strsplit(s, line, '\t');
  sprintf(line, "SHOW '%s'", s[0]);
  exit(line);
}

void dialog(void) {
  int select = 0;
  int ssort = 0;
  if (argc < 2) ssort = 1;

  int Result = dlgDialog("Wire length of Layout") {
    dlgListView("", data, select, ssort) show(data[select]);
    dlgHBoxLayout {
      dlgPushButton("+Ok") dlgReject();
      dlgPushButton(SButton) {
        board(B) {
          string FileName = dlgFileSave("Save list", filesetext(B.name, ".txt"));
          if (FileName) {
            output (FileName, "wt") {
              printf("%s", header);
              for (int x = 0; x < t; x++) printf("%s\n", data[x]);
            }
          }
        }
      }
      dlgStretch(1);
      dlgPushButton(HButton) help();
    }
  };
  if (!Result)
     exit(0);
}


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


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


int found(string signame) {
  if (argc < 2) {
    return 1;
  }
  else if (argc == 2 && argv[1] == "*") return 1;
  int f = 0;
  int sig = 0;
  for (int n = 0; n <= sig_n; n++) {

    if (strchr(signal_list[n], '*') >= 0) {  // wildcard * in name
      int l = strlen(signal_list[n]);
      string s;

      if (signal_list[n][0] == '*' && signal_list[n][l-1] == '*') {  // *name*
        s = strsub(signal_list[n], 1, l-2 );
        if(strstr(signame, s) > 0) {
          f = 1;
          break;
        }
      }

      else if (signal_list[n][l-1] == '*') {      // name*
        s = strsub(signal_list[n], 0, l-1);
        if(strstr(signame, s) == 0) {
          f = 1;
          break;
        }
      }

      else if (signal_list[n][0] == '*') {      // *name
        s = strsub(signal_list[n], 1);
        if(strstr(signame, s) > 0) {
          f = 1;
          break;
        }
      }
    }
    else if (signame == signal_list[n]) {
      f = 1;
      break;
    }
  }
  return f;
}

// *** 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;
}

// *** Unroutet length ***
string unroute(real l, int cnt) {
  string s;
  if (cnt) sprintf(s, "%.f", l);
  else s = "--";
  return s;
}



// *** main ***
if (board) board(B) {
  if (argc < 2) { // 2008-09-11 alf@cadsoft.de
    help();
  }
  else if (argc == 2 && argv[1] == "*") ;
  else {
    string h;
    int n;
    string list;

    // *** check only by signal name ***
    sig_n = argc -2;
    for (n = 1; n < argc; n++) {
      signal_list[sig_n] = strupr(argv[n]);
      list += signal_list[sig_n] + "\n";
      sig_n++;
    }
  }

  sprintf(h, "%s\n", EAGLE_SIGNATURE);
  header += h;
  sprintf(h, "List of signals with length");
  header += h;

  string Signal[];
  int n = 0;
  B.signals(S) {
    if (found(S.name)) {
      WLtotal = 0;
      int cntUnroute = 0;
      real unroute = 0;
      S.wires(W) {
        if (W.layer < 17) {           // nur Kupfer-Layer
          if (W.arc) {
            WLtotal += ArcLength(W.arc.angle1, W.arc.angle2, u2mm(W.arc.radius));
          }
          else {
            WLtotal += WireLength(u2mm(W.x2), u2mm(W.x1), u2mm(W.y2), u2mm(W.y1));
          }
        }
        if (W.layer == 19) {           // unrouted Layer
          unroute += WireLength(u2mm(W.x2), u2mm(W.x1), u2mm(W.y2), u2mm(W.y1));
          cntUnroute++;
        }
      }
      Signal[n] = S.name;
      Length[n] = WLtotal;
      Unroute_length[n] = unroute;
      Unroute_cnt[n] = cntUnroute;
      ++n;
    }
  }

  sort(n, index, Length);
  sprintf(h, "Signal\tl [mm]\tdiff. [mm]\tdiff. [%%]\tunrouted [mm]");
  data[0] = h;
  t = 1;
  real null_length = Length[index[0]];

  for (int i = 0; i < n; ++i) {
    sprintf(h, "%s\t%.9f\t%.9f\t%s\t%s",
                Signal[index[i]],
                Length[index[i]],
                Length[index[i]] - null_length,
                percent(Length[index[i]], null_length),
                unroute(Unroute_length[index[i]], Unroute_cnt[index[i]])
           );
    data[t] = h;
    t++;
  }
  dialog();
}

else {
   dlgMessageBox("Start this ULP in a Board");
   exit (0);
}