#usage "<b>Eagle board (brd) exporting tool version 1.0 </b>\n"
       "<p>"
       "This ULP will convert an Eagle board (brd) file to an intermediate xml file suitable for further processing."
       "<p>"
       "Load any board (brd file) and execute the ULP."
       "<p>"
       "<author>Author: jonathan.cohen (at) ixds.de</author><br>"
       "based on eagle2svg by nils.springob (at) nicai-systems.de<br>"
       "<br>"
       "</ul>"
       
real VERSION   = 1.0;

// RUN brd2xml

/*
 * CHANGELOG================================================
 *
 *
 */  

/* ==========================================================================
 * License: This file is released under the license of the GNU Public license
 *          Version 2 and 3.
 * ==========================================================================*/   


//------------------------------------------------------
// replace xml reserved chars and build 2-byte utf-8 characters  
//------------------------------------------------------
string encodeText(string strText) {
  string newText = "";
  int i=0;
  
  while (strText[i]) {
    char c = strText[i++];
    switch(c) {
      case '<': newText += "&lt;"; break; 
      case '>': newText += "&gt;"; break;
      case '&': newText += "&amp;"; break;
	  case '"': newText += "&quot;"; break;
	  case '\'': newText += "&apos;"; break;
      default:
        if (c>=0xc0) {
          newText += char(0xc3);
          newText += char(c-0x40);
        } else if (c>=0x80) {
          newText += char(0xc2);
          newText += char(c);
        } else {
          newText += c;
        }
    }
  }
  return newText;
}


void xmlWriteCircle(UL_CIRCLE C) {
  printf("<circle cx='%fmil' cy='%fmil' r='%fmil' width='%fmil' layer='%d'/>\n", 
    u2mil(C.x), u2mil(C.y), u2mil(C.radius), u2mil(C.width), C.layer);
}


//------------------------------------------------------
// write UL rectangle
//------------------------------------------------------
void xmlWriteRectangle(UL_RECTANGLE R) {
    printf("<rect x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' layer='%d' angle='%f'/>\n",
    u2mil(R.x1), u2mil(R.y1), 
    u2mil(R.x2), u2mil(R.y2),
    R.layer, R.angle);
}


//------------------------------------------------------
// write UL wire as arc
//------------------------------------------------------
void xmlWriteWireArc(UL_WIRE W) {
  printf("<arc angle1='%f' angle2='%f' cap='%d' layer='%d' r='%fmil' width='%fmil' x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' xc='%fmil' yc='%fmil' />\n",
	W.arc.angle1, W.arc.angle2, W.arc.cap, W.arc.layer, 
	u2mil(W.arc.radius), u2mil(W.arc.width),
    	u2mil(W.arc.x1), u2mil(W.arc.y1),
    	u2mil(W.arc.x2), u2mil(W.arc.y2),    
	u2mil(W.arc.xc), u2mil(W.arc.yc)
	);
}


//------------------------------------------------------
// write UL wire as line
//------------------------------------------------------
void xmlWriteWireLine(UL_WIRE W) {
  printf("<line x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' width='%fmil' layer='%d' cap='%d'/>\n",
    u2mil(W.x1), u2mil(W.y1), 
    u2mil(W.x2), u2mil(W.y2),
    u2mil(W.width),
    W.layer, W.cap);
}

//------------------------------------------------------
// write UL piece
//------------------------------------------------------
void xmlWritePiece(UL_WIRE W) {
 printf("<piece>\n");
  if (W.arc) {
    xmlWriteWireArc(W);
  } else {
    xmlWriteWireLine(W);
  }
 printf("</piece>\n"); 
}


//------------------------------------------------------
// write UL wire
//------------------------------------------------------
void xmlWriteWire(UL_WIRE W) {

  printf("<wire cap='%d' curve='%f' x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' width='%fmil' style='%d' layer='%d'>\n",
			W.cap, W.curve, u2mil(W.x1), u2mil(W.y1), u2mil(W.x2), u2mil(W.y2), u2mil(W.width), W.style, W.layer);
  if ( W.style == WIRE_STYLE_LONGDASH || W.style == WIRE_STYLE_SHORTDASH || W.style == WIRE_STYLE_DASHDOT ) {
    W.pieces(P) {
      xmlWritePiece(P);           
    }      
  } else {
    xmlWritePiece(W);
  }
  printf("</wire>\n");
}



//------------------------------------------------------
// write UL general text
//------------------------------------------------------
void xmlWriteText(UL_TEXT T) {

  int width = 0;
  T.wires(W) {
		if (W.width > width) width = W.width;
  }

  string text = "<text angle='%f' font='%d' layer='%d' mirror='%d' size='%fmil' spin='%d' x='%fmil' y='%fmil' width='%fmil' ratio='%f'>\n";
  printf(text, T.angle, T.font, T.layer, T.mirror, u2mil(T.size), T.spin, u2mil(T.x), u2mil(T.y), u2mil(width), T.ratio / 100.0);
  printf("<value>%s</value>\n", encodeText(T.value));

  if (T.font==FONT_VECTOR) {
     printf("<wires>\n");
	 T.wires(W) {
		xmlWriteWire(W);
	 }
	 printf("</wires>\n");

  }
  printf("</text>\n");

}

//------------------------------------------------------
// write UL pad
//------------------------------------------------------
void xmlWritePad(UL_PAD P) {
  printf("<pad angle='%f' drill='%fmil' drillsymbol='%d' elongation='%d' flags='%d' name='%s' signal='%s' x='%fmil' y='%fmil'>\n", 
		P.angle, u2mil(P.drill), P.drillsymbol, P.elongation, P.flags, P.name, P.signal, u2mil(P.x), u2mil(P.y));

  int layers[];
  layers[0] = LAYER_TOP;
  layers[1] = LAYER_BOTTOM;
  layers[2] = LAYER_PADS;			
  layers[3] = LAYER_TSTOP;
  layers[4] = LAYER_BSTOP;
  for (int i = 0; i < 5; i++) {
	int l = layers[i];
	string shapeString="";
	int shape = P.shape[l];
	switch(shape) {
		case PAD_SHAPE_SQUARE: shapeString = "square"; break; 
		case PAD_SHAPE_ROUND: shapeString = "round"; break; 
		case PAD_SHAPE_OCTAGON: shapeString = "octagon"; break; 
		case PAD_SHAPE_LONG: shapeString = "long"; break; 
		case PAD_SHAPE_OFFSET: shapeString = "offset"; break; 
		case PAD_SHAPE_ANNULUS: shapeString = "annulus"; break; 
		case PAD_SHAPE_THERMAL: shapeString = "thermal"; break; 
	}
	printf("<layer layer='%d' diameter='%fmil' shape='%s' elongation='%d'/>\n", l, u2mil(P.diameter[l]), shapeString, P.elongation);
  }

  printf("</pad>\n");
}


//------------------------------------------------------
// write UL smd
//------------------------------------------------------
void xmlWriteSmd(UL_SMD S) {
  printf("<smd angle='%f' flags='%d' name='%s' signal='%s' x='%fmil' y='%fmil' layer='%d' roundness='%d' dx='%fmil' dy='%fmil' >\n", 
		S.angle, S.flags, S.name, S.signal, u2mil(S.x), u2mil(S.y), S.layer, S.roundness, u2mil(S.dx), u2mil(S.dy));
  int layers[];
  if (S.layer==LAYER_TOP) {
	layers[0] = LAYER_TOP;
	layers[1] = LAYER_TCREAM;		 
	layers[2] = LAYER_TSTOP;
  }
  else if (S.layer==LAYER_BOTTOM) {
	layers[0] = LAYER_BOTTOM;
	layers[1] = LAYER_BCREAM;			
	layers[2] = LAYER_BSTOP;
  }
  for (int i = 0; i < 3; i++) {
	int l = layers[i];
	printf("<layer layer='%d' dx='%fmil' dy='%fmil' />\n", l, u2mil(S.dx[l]), u2mil(S.dy[l]));
  }
  printf("</smd>\n");
}


//------------------------------------------------------
// write UL via
//------------------------------------------------------
void xmlWriteVia(UL_VIA V) {
  printf("<via drill='%fmil' drillsymbol='%d' flags='%d' x='%fmil' y='%fmil'>\n",
			u2mil(V.drill), V.drillsymbol, V.flags, u2mil(V.x), u2mil(V.y));
  int layers[];
  layers[0] = LAYER_TOP;
  layers[1] = LAYER_BOTTOM;
  layers[2] = LAYER_VIAS;			
  layers[3] = LAYER_TSTOP;
  layers[4] = LAYER_BSTOP;
  for (int i = 0; i < 5; i++) {
	int l = layers[i];
	if (l >= V.start && l < V.end) {	
		string shapeString="";
		int shape = V.shape[l];
		switch(shape) {
			case VIA_SHAPE_SQUARE: shapeString = "square"; break; 
			case VIA_SHAPE_ROUND: shapeString = "round"; break; 
			case VIA_SHAPE_OCTAGON: shapeString = "octagon"; break; 
			case VIA_SHAPE_ANNULUS: shapeString = "annulus"; break; 
			case VIA_SHAPE_THERMAL: shapeString = "thermal"; break; 
		}
		printf("<layer layer='%d' diameter='%fmil' shape='%s'/>\n", l, u2mil(V.diameter[l]), shapeString);
	}
  }
  printf("</via>\n");
}

//------------------------------------------------------
//write UL contact
//------------------------------------------------------
void xmlWriteContact(UL_CONTACT C) {
  printf("<contact name='%s' signal='%s' x='%fmil' y='%fmil'>\n", encodeText(C.name), encodeText(C.signal), u2mil(C.x), u2mil(C.y));
  if (C.pad) {
    xmlWritePad(C.pad);
  } else if (C.smd) {
    xmlWriteSmd(C.smd);    
  }
  printf("</contact>\n");
}

//------------------------------------------------------
// write UL hole
//------------------------------------------------------
void xmlWriteHole(UL_HOLE H) { 
  printf("<hole x='%fmil' y='%fmil' drill='%fmil' drillsymbol='%d' t_diameter='%fmil' b_diameter='%fmil' />\n",
			u2mil(H.x), u2mil(H.y), u2mil(H.drill), H.drillsymbol, u2mil(H.diameter[LAYER_TSTOP]), u2mil(H.diameter[LAYER_BSTOP]));
}

//------------------------------------------------------
// write UL polygon
//------------------------------------------------------
void xmlWritePolygon(UL_POLYGON P) {

  int count = 0;
  printf( "<polygon layer='%d' isolate='%d' orphans='%d' pour='%d' rank='%d' spacing='%d' thermals='%d' width='%fmil' >\n",
	P.layer, P.isolate, P.orphans, P.pour, P.rank, P.spacing, P.thermals, u2mil(P.width));

  printf( "<contours>\n" );
  P.contours(W) {
    xmlWriteWire(W);
  }
  printf( "</contours>\n" );
  printf( "<fillings>\n" );
  P.fillings(W) {
    xmlWriteWire(W);
  }
  printf( "</fillings>\n" );

  printf( "</polygon>\n" );

}

void xmlWriteFrame(UL_FRAME F) {
	printf("<frame layer='%d' x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' />\n",
	F.layer, u2mil(F.x1), u2mil(F.y1), u2mil(F.x2), u2mil(F.y2));
}

//------------------------------------------------------
//write UL package
//------------------------------------------------------
void xmlWritePackage(UL_PACKAGE PKG) {
  printf("<package x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' description='%s' headline='%s' library='%s' name='%s' >\n",
		u2mil(PKG.area.x1), 
		u2mil(PKG.area.y1), 
		u2mil(PKG.area.x2), 
		u2mil(PKG.area.y2), 
		encodeText(PKG.description), 
		encodeText(PKG.headline), 
		encodeText(PKG.library), 
		encodeText(PKG.name)
	);

  printf("<polygons>\n"); 	 
  PKG.polygons(P)   {xmlWritePolygon(P);}
  printf("</polygons>\n"); 

  printf("<circles>\n"); 
  PKG.circles(C)    {xmlWriteCircle(C);}
  printf("</circles>\n"); 

  printf("<rects>\n"); 
  PKG.rectangles(R) {xmlWriteRectangle(R);}
  printf("</rects>\n"); 

  printf("<wires>\n"); 
  PKG.wires(W)      {xmlWriteWire(W);}
  printf("</wires>\n"); 

  printf("<texts>\n"); 
  PKG.texts(T)      {xmlWriteText(T);}
  printf("</texts>\n"); 

  printf("<contacts>\n"); 
  PKG.contacts(C)   {xmlWriteContact(C);}
  printf("</contacts>\n"); 

  printf("<holes>\n"); 
  PKG.holes(H)      {xmlWriteHole(H);}
  printf("</holes>\n"); 

  printf("<frames>\n"); 
  PKG.frames(F)     {xmlWriteFrame(F);}
  printf("</frames>\n"); 

  printf("</package>\n");
}

//------------------------------------------------------
//write UL attribute
//------------------------------------------------------
void xmlWriteAttribute(UL_ATTRIBUTE A) {
	printf("<attribute name='%s' value='%s'>\n", encodeText(A.name), encodeText(A.value));
	xmlWriteText(A.text);
	printf("</attribute>");
}


//------------------------------------------------------
//write UL element
//------------------------------------------------------
void xmlWriteElement(UL_ELEMENT E) {
  printf("<element name='%s' angle='%f' column='%s' row='%s' mirror='%d' spin='%d' value='%s' x='%fmil' y='%fmil' >\n", 
	encodeText(E.name), E.angle, E.column, E.row, E.mirror, E.spin, encodeText(E.value), u2mil(E.x), u2mil(E.y));  
  
  printf("<attributes>\n");
  E.attributes(A) {xmlWriteAttribute(A);}
  printf("</attributes>\n");

  printf("<texts>\n");    
  E.texts(T) {xmlWriteText(T);}
  printf("</texts>\n");

  xmlWritePackage(E.package);
  printf("</element>\n");      
}

//------------------------------------------------------
//write UL contactref
//------------------------------------------------------
void xmlWriteContactref(UL_CONTACTREF C) {
   printf("<contactref elementname='%s' contactname='%s'/>\n", C.element.name, C.contact.name); 
}

//------------------------------------------------------
//write UL signal
//------------------------------------------------------
void xmlWriteSignal(UL_SIGNAL S) {
  printf("<signal name='%s' classname='%s'>\n", S.name, S.class.name); 
  S.contactrefs(C) {xmlWriteContactref(C);} 
  S.polygons(P) {xmlWritePolygon(P);}    
  S.wires(W)    {xmlWriteWire(W);}
  S.vias(V)     {xmlWriteVia(V);} 
  printf("</signal>\n"); 
}

//------------------------------------------------------
//write UL layer
//------------------------------------------------------
void xmlWriteLayer(UL_LAYER L) {
  
  printf("<layer color='%d' fill='%d' name='%s' number='%d' used='%d' visible='%d' />\n", 
		L.color, L.fill, L.name, L.number, L.used, L.visible);
}


//------------------------------------------------------
//write xml board header
//------------------------------------------------------
void xmlWriteBoardHeader(UL_BOARD B) {
  string header =  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  header += "<board version='1.0' ";
  header += "x1='%fmil' y1='%fmil' x2='%fmil' y2='%fmil' >\n";

  printf(header, u2mil(B.area.x1), u2mil(B.area.y1), u2mil(B.area.x2), u2mil(B.area.y2));

  printf("<title>%s</title>\n", encodeText(B.name));
  printf("<desc>generated by brd2xml %.1f, developed by http://www.ixds.de</desc>\n", VERSION);
}


//------------------------------------------------------
//write svg board footer
//------------------------------------------------------
void xmlWriteBoardFooter(UL_BOARD B) {
  string footer = "</board>\n";
  printf(footer);
}


//------------------------------------------------------
//write whole board 
//------------------------------------------------------
void xmlWriteBoard(string fileName) {
  output(fileName, "Fwt") {
    if(board) {
      board(B) {
        xmlWriteBoardHeader(B);
        
		printf("<circles>\n");
        B.circles(C)    { xmlWriteCircle(C); }       
		printf("</circles>\n");

		printf("<texts>\n");
		B.texts(T)      { xmlWriteText(T); }
		printf("</texts>\n");

		printf("<rects>\n");
        B.rectangles(R) { xmlWriteRectangle(R); }
		printf("</rects>\n");

		printf("<polygons>\n");
        B.polygons(P)   { xmlWritePolygon(P); }
		printf("</polygons>\n");

		printf("<wires>\n");
        B.wires(W)      { xmlWriteWire(W); }
		printf("</wires>\n");

		printf("<elements>\n");
        B.elements(E)   { xmlWriteElement(E); }
		printf("</elements>\n");

		printf("<holes>\n");
        B.holes(H)      { xmlWriteHole(H); }
		printf("</holes>\n");

		printf("<signals>\n");
        B.signals(S)    { xmlWriteSignal(S); }
		printf("</signals>\n");

		printf("<frames>\n");
		B.frames(F)		{ xmlWriteFrame(F); }
		printf("</frames>\n");

		printf("<layers>\n");
		B.layers(L)	    { xmlWriteLayer(L); }
		printf("</layers>\n");
		 
        xmlWriteBoardFooter(B);
      }
    }  
  }   
}


//------------------------------------------------------
// Global 
//------------------------------------------------------
string g_path_name ;   


//------------------------------------------------------
// Select the path where the xml will be saved 
//------------------------------------------------------
void openPath(string startPath) {
   string dirName = "";
   string stringArray[];
   dirName = dlgDirectory("Select a directory", startPath);
   
   if( dirName != "" ) {
      board(B) {
         int n = 0;
         string tmpName = B.name; 
         int nr = strsplit(stringArray, tmpName, '/');
         g_path_name = dirName + "/" + stringArray[nr-1];
         g_path_name = strsub(g_path_name , 0, strlen(g_path_name) - 4) + ".xml";
      }
   }
}

int displayDialog(string titleStr) {
  int space = 10;
  int result = dlgDialog(titleStr) {
    dlgVBoxLayout {
      dlgVBoxLayout {

        dlgHBoxLayout dlgSpacing(500);
        dlgStretch(0);
        dlgSpacing(space);
        dlgLabel("Export to file:");
        dlgStretch(0);
        dlgHBoxLayout {
          dlgSpacing(space);
          dlgStringEdit(g_path_name);
          dlgSpacing(space);
          dlgPushButton("...") openPath("C:\\");
          dlgSpacing(space);
        }
        dlgStretch(10);
        dlgLabel("developed by <a href='http://www.ixds.de'>http://ixds.de</a>, based on eagle2svg");
      }
      dlgStretch(0);
      dlgHBoxLayout {
        dlgStretch(1);
        dlgPushButton("+OK") dlgAccept();
        dlgSpacing(space);
        dlgPushButton("-Cancel") dlgReject();
        dlgStretch(0);
        dlgSpacing(space);
      }
      dlgStretch(10);
    }
  };
  return result;
}


//------------------------------------------------------
// main program 
//------------------------------------------------------
  string ref;
  string titleStr;
  
  sprintf(titleStr, "Export Eagle board to XML, Version: %.1f", VERSION);

  if (board) {
    if (argc > 1) {
	 string fn = argv[1];
	 //dlgMessageBox(fn);
	 xmlWriteBoard(fn);
   }
   else { 
	board(B) {
		sprintf(g_path_name, "%s_brd.xml", strsub(B.name, 0, strlen(B.name) - 4));    
	}
	displayDialog(titleStr);
	xmlWriteBoard(g_path_name);
    }
	
    exit(EXIT_SUCCESS);

  } else {
    dlgMessageBox("Please run from board editor." ); 
    exit(EXIT_FAILURE);
  }

