/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  (this file is based on PCB, interactive printed circuit board design)
 *
 *  NELMA (Numerical capacitance calculator) export HID
 *  Copyright (C) 2006 Tomaz Solc (tomaz.solc@tablix.org)
 *
 *  PNG export code is based on the PNG export HID
 *  Copyright (C) 2006 Dan McMahill
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/pcb-rnd
 *    lead developer: email to pcb-rnd (at) igor2.repo.hu
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

/*
 * This HID exports a PCB layout into: o One layer mask file (PNG format) per
 * copper layer. o Nelma configuration file that contains netlist and pin
 * information.
 */

/*
 * FIXME:
 *
 * If you have a section of a net that does not contain any pins then that
 * section will be missing from the Nelma's copper geometry.
 *
 * For example:
 *
 * this section will be ignored by Nelma |       |
 *
 * ||             ||=======||            ||     component layer ||
 * ||       ||            || ||=============||       ||============||
 * solder layer
 *
 * pin1           via      via           pin2
 *
 * Single layer layouts are always exported correctly.
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <time.h>

#include "board.h"
#include "error.h"
#include "data.h"
#include "layer.h"
#include "rats.h"
#include "plugins.h"
#include "hid_cam.h"
#include "safe_fs.h"

#include "hid.h"
#include "hid_nogui.h"
#include "hid_draw_helpers.h"

#include <gd.h>

#include "hid_init.h"
#include "hid_attrib.h"
#include "hid_flags.h"
#include "hid_color.h"

const char *nelma_cookie = "nelma HID";

#define CRASH(func) fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", func); abort()

/* Needed for PNG export */

struct color_struct {
	/* the descriptor used by the gd library */
	int c;

	/* so I can figure out what rgb value c refers to */
	unsigned int r, g, b;
};

struct hid_gc_s {
	pcb_core_gc_t core_gc;
	pcb_hid_t *me_pointer;
	pcb_cap_style_t cap;
	pcb_coord_t width;
	unsigned char r, g, b;
	int erase;
	int faded;
	struct color_struct *color;
	gdImagePtr brush;
};

static pcb_hid_t nelma_hid;

static struct color_struct *black = NULL, *white = NULL;
static pcb_coord_t linewidth = -1;
static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1);
static int lastcolor = -1;

/* gd image and file for PNG export */
static gdImagePtr nelma_im = NULL;
static FILE *nelma_f = NULL;

static int is_mask;
static int is_drill;

/*
 * Which groups of layers to export into PNG layer masks. 1 means export, 0
 * means do not export.
 */
static int nelma_export_group[PCB_MAX_LAYERGRP];

/* Group that is currently exported. */
static int nelma_cur_group;

/* Filename prefix that will be used when saving files. */
static const char *nelma_basename = NULL;

/* Horizontal DPI (grid points per inch) */
static int nelma_dpi = -1;

/* Height of the copper layers in micrometers. */

/*
 * The height of the copper layer is currently taken as the vertical grid
 * step, since this is the smallest vertical feature in the layout.
 */
static int nelma_copperh = -1;
/* Height of the substrate layers in micrometers. */
static int nelma_substrateh = -1;
/* Relative permittivity of the substrate. */
static double nelma_substratee = -1;

/* Permittivity of empty space (As/Vm) */
static const double nelma_air_epsilon = 8.85e-12;

pcb_hid_attribute_t nelma_attribute_list[] = {
	/* other HIDs expect this to be first.  */

/* %start-doc options "nelma Options"
@ftable @code
@item -- basename <string>
File name prefix.
@end ftable
%end-doc
*/
	{"basename", "File name prefix",
	 PCB_HATT_STRING, 0, 0, {0, 0, 0}, 0, 0},
#define HA_basename 0

/* %start-doc options "nelma Options"
@ftable @code
@item --dpi <num>
Horizontal scale factor (grid points/inch).
@end ftable
%end-doc
*/
	{"dpi", "Horizontal scale factor (grid points/inch)",
	 PCB_HATT_INTEGER, 0, 1000, {100, 0, 0}, 0, 0},
#define HA_dpi 1

/* %start-doc options "nelma Options"
@ftable @code
@item --copper-height <num>
Copper layer height (um).
@end ftable
%end-doc
*/
	{"copper-height", "Copper layer height (um)",
	 PCB_HATT_INTEGER, 0, 200, {100, 0, 0}, 0, 0},
#define HA_copperh 2

/* %start-doc options "nelma Options"
@ftable @code
@item --substrate-height <num>
Substrate layer height (um).
@end ftable
%end-doc
*/
	{"substrate-height", "Substrate layer height (um)",
	 PCB_HATT_INTEGER, 0, 10000, {2000, 0, 0}, 0, 0},
#define HA_substrateh 3

/* %start-doc options "nelma Options"
@ftable @code
@item --substrate-epsilon <num>
Substrate relative epsilon.
@end ftable
%end-doc
*/
	{"substrate-epsilon", "Substrate relative epsilon",
	 PCB_HATT_REAL, 0, 100, {0, 0, 4.0}, 0, 0},
#define HA_substratee 4
};

#define NUM_OPTIONS (sizeof(nelma_attribute_list)/sizeof(nelma_attribute_list[0]))

PCB_REGISTER_ATTRIBUTES(nelma_attribute_list, nelma_cookie)
		 static pcb_hid_attr_val_t nelma_values[NUM_OPTIONS];

/* *** Utility funcions **************************************************** */

/* convert from default PCB units to nelma units */
		 static int pcb_to_nelma(pcb_coord_t pcb)
{
	return PCB_COORD_TO_INCH(pcb) * nelma_dpi;
}

static char *nelma_get_png_name(const char *basename, const char *suffix)
{
	char *buf;
	int len;

	len = strlen(basename) + strlen(suffix) + 6;
	buf = (char *) malloc(sizeof(*buf) * len);

	sprintf(buf, "%s.%s.png", basename, suffix);

	return buf;
}

/* *** Exporting netlist data and geometry to the nelma config file ******** */

static void nelma_write_space(FILE * out)
{
	double xh, zh;
	int z, i;

	xh = 2.54e-2 / ((double) nelma_dpi);
	zh = nelma_copperh * 1e-6;

	fprintf(out, "\n/* **** Space **** */\n\n");

	fprintf(out, "space pcb {\n");
	fprintf(out, "\tstep = { %e, %e, %e }\n", xh, xh, zh);
	fprintf(out, "\tlayers = {\n");

	fprintf(out, "\t\t\"air-top\",\n");
	fprintf(out, "\t\t\"air-bottom\"");

	z = 10;
	for (i = 0; i < PCB_MAX_LAYERGRP; i++)
		if (nelma_export_group[i]) {
			char tmp_ln[PCB_PATH_MAX];
			const char *ext = pcb_layer_to_file_name(tmp_ln, -1, pcb_layergrp_flags(PCB, i), PCB_FNS_fixed);

			if (z != 10) {
				fprintf(out, ",\n");
				fprintf(out, "\t\t\"substrate-%d\"", z);
				z++;
			}
			fprintf(out, ",\n");
			fprintf(out, "\t\t\"%s\"", ext);
			z++;
		}
	fprintf(out, "\n\t}\n");
	fprintf(out, "}\n");
}


static void nelma_write_material(FILE * out, const char *name, const char *type, double e)
{
	fprintf(out, "material %s {\n", name);
	fprintf(out, "\ttype = \"%s\"\n", type);
	fprintf(out, "\tpermittivity = %e\n", e);
	fprintf(out, "\tconductivity = 0.0\n");
	fprintf(out, "\tpermeability = 0.0\n");
	fprintf(out, "}\n");
}

static void nelma_write_materials(FILE * out)
{
	fprintf(out, "\n/* **** Materials **** */\n\n");

	nelma_write_material(out, "copper", "metal", nelma_air_epsilon);
	nelma_write_material(out, "air", "dielectric", nelma_air_epsilon);
	nelma_write_material(out, "composite", "dielectric", nelma_air_epsilon * nelma_substratee);
}

static void nelma_write_nets(FILE * out)
{
	pcb_lib_t netlist;
	pcb_lib_menu_t *net;
	pcb_lib_entry_t *pin;

	int n, m, i;

	netlist = PCB->NetlistLib[PCB_NETLIST_EDITED];

	fprintf(out, "\n/* **** Nets **** */\n\n");

	for (n = 0; n < netlist.MenuN; n++) {
		net = &netlist.Menu[n];

		/* Weird, but correct */
		fprintf(out, "net %s {\n", &net->Name[2]);

		fprintf(out, "\tobjects = {\n");

		for (m = 0; m < net->EntryN; m++) {
			pin = &net->Entry[m];

			/* pcb_pin_name_to_xy(pin, &x, &y); */

			for (i = 0; i < PCB_MAX_LAYERGRP; i++)
				if (nelma_export_group[i]) {
					char tmp_ln[PCB_PATH_MAX];
					const char *ext = pcb_layer_to_file_name(tmp_ln, -1, pcb_layergrp_flags(PCB, i), PCB_FNS_fixed);

					if (m != 0 || i != 0)
						fprintf(out, ",\n");
					fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, ext);
				}
		}

		fprintf(out, "\n");
		fprintf(out, "\t}\n");
		fprintf(out, "}\n");
	}
}

static void nelma_write_layer(FILE * out, int z, int h, const char *name, int full, const char *mat)
{
	pcb_lib_t netlist;
	pcb_lib_menu_t *net;
	pcb_lib_entry_t *pin;

	int n, m;

	fprintf(out, "layer %s {\n", name);
	fprintf(out, "\theight = %d\n", h);
	fprintf(out, "\tz-order = %d\n", z);
	fprintf(out, "\tmaterial = \"%s\"\n", mat);

	if (full) {
		fprintf(out, "\tobjects = {\n");
		netlist = PCB->NetlistLib[PCB_NETLIST_EDITED];

		for (n = 0; n < netlist.MenuN; n++) {
			net = &netlist.Menu[n];

			for (m = 0; m < net->EntryN; m++) {
				pin = &net->Entry[m];

				if (m != 0 || n != 0)
					fprintf(out, ",\n");
				fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, name);
			}

		}
		fprintf(out, "\n\t}\n");
	}
	fprintf(out, "}\n");
}

static void nelma_write_layers(FILE * out)
{
	int i, subh, z;
	char buf[100];

	subh = nelma_substrateh / nelma_copperh;

	fprintf(out, "\n/* **** Layers **** */\n\n");

	/* Air layers on top and bottom of the stack */
	/* Their height is double substrate height. */
	nelma_write_layer(out, 1, 2 * subh, "air-top", 0, "air");
	nelma_write_layer(out, 1000, 2 * subh, "air-bottom", 0, "air");

	z = 10;
	for (i = 0; i < PCB_MAX_LAYERGRP; i++)
		if (nelma_export_group[i]) {
			char tmp_ln[PCB_PATH_MAX];
			const char *ext = pcb_layer_to_file_name(tmp_ln, -1, pcb_layergrp_flags(PCB, i), PCB_FNS_fixed);

			if (z != 10) {
				sprintf(buf, "substrate-%d", z);
				nelma_write_layer(out, z, subh, buf, 0, "composite");
				z++;
			}
			/*
			 * FIXME: for layers that are not on top or bottom,
			 * the material should be "composite"
			 */
			nelma_write_layer(out, z, 1, ext, 1, "air");

			z++;
		}
}

static void nelma_write_object(FILE * out, pcb_lib_entry_t *pin)
{
	pcb_coord_t px = 0, py = 0;
	int x, y, i;
	char *f;

	pcb_pin_name_to_xy(pin, &px, &py);

	x = pcb_to_nelma(px);
	y = pcb_to_nelma(py);

	for (i = 0; i < PCB_MAX_LAYERGRP; i++)
		if (nelma_export_group[i]) {
			char tmp_ln[PCB_PATH_MAX];
			const char *ext = pcb_layer_to_file_name(tmp_ln, -1, pcb_layergrp_flags(PCB, i), PCB_FNS_fixed);

			fprintf(out, "object %s-%s {\n", pin->ListEntry, ext);
			fprintf(out, "\tposition = { 0, 0 }\n");
			fprintf(out, "\tmaterial = \"copper\"\n");
			fprintf(out, "\ttype = \"image\"\n");
			fprintf(out, "\trole = \"net\"\n");

			f = nelma_get_png_name(nelma_basename, ext);

			fprintf(out, "\tfile = \"%s\"\n", f);

			free(f);

			fprintf(out, "\tfile-pos = { %d, %d }\n", x, y);
			fprintf(out, "}\n");
		}
}

static void nelma_write_objects(FILE * out)
{
	pcb_lib_t netlist;
	pcb_lib_menu_t *net;
	pcb_lib_entry_t *pin;

	int n, m;

	netlist = PCB->NetlistLib[PCB_NETLIST_EDITED];

	fprintf(out, "\n/* **** Objects **** */\n\n");

	for (n = 0; n < netlist.MenuN; n++) {
		net = &netlist.Menu[n];

		for (m = 0; m < net->EntryN; m++) {
			pin = &net->Entry[m];

			nelma_write_object(out, pin);
		}
	}
}

/* *** Main export callback ************************************************ */

static int nelma_parse_arguments(int *argc, char ***argv)
{
	pcb_hid_register_attributes(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]), nelma_cookie, 0);
	return pcb_hid_parse_command_line(argc, argv);
}

static pcb_hid_attribute_t *nelma_get_export_options(int *n)
{
	static char *last_made_filename = 0;

	if (PCB) {
		pcb_derive_default_filename(PCB->Filename, &nelma_attribute_list[HA_basename], ".nelma", &last_made_filename);
	}
	if (n) {
		*n = NUM_OPTIONS;
	}
	return nelma_attribute_list;
}

/* Populates nelma_export_group array */
void nelma_choose_groups()
{
	int n, m;
	pcb_layer_t *layer;

	/* Set entire array to 0 (don't export any layer groups by default */
	memset(nelma_export_group, 0, sizeof(nelma_export_group));

	for (n = 0; n < pcb_max_layer; n++) {
		unsigned int flags = pcb_layer_flags(PCB, n);
		if (flags & PCB_LYT_SILK)
			continue;
		layer = &PCB->Data->Layer[n];

		if (!pcb_layer_is_empty_(PCB, layer)) {
			/* layer isn't empty */
			if ((flags & PCB_LYT_COPPER) || (flags & PCB_LYT_OUTLINE)) {
				/* layer is a copper layer */
				m = pcb_layer_get_group(PCB, n);

				/* the export layer */
				nelma_export_group[m] = 1;
			}
		}
	}
}

static void nelma_alloc_colors()
{
	/*
	 * Allocate white and black -- the first color allocated becomes the
	 * background color
	 */

	white = (struct color_struct *) malloc(sizeof(*white));
	white->r = white->g = white->b = 255;
	white->c = gdImageColorAllocate(nelma_im, white->r, white->g, white->b);

	black = (struct color_struct *) malloc(sizeof(*black));
	black->r = black->g = black->b = 0;
	black->c = gdImageColorAllocate(nelma_im, black->r, black->g, black->b);
}

static void nelma_start_png(const char *basename, const char *suffix)
{
	int h, w;
	char *buf;

	buf = nelma_get_png_name(basename, suffix);

	h = pcb_to_nelma(PCB->MaxHeight);
	w = pcb_to_nelma(PCB->MaxWidth);

	/* nelma_im = gdImageCreate (w, h); */

	/* Nelma only works with true color images */
	nelma_im = gdImageCreate(w, h);
	nelma_f = pcb_fopen(buf, "wb");

	nelma_alloc_colors();

	free(buf);
}

static void nelma_finish_png()
{
#ifdef HAVE_GDIMAGEPNG
	gdImagePng(nelma_im, nelma_f);
#else
	pcb_message(PCB_MSG_WARNING, "NELMA: PNG not supported by gd. Can't write layer mask.\n");
#endif
	gdImageDestroy(nelma_im);
	fclose(nelma_f);

	free(white);
	free(black);

	nelma_im = NULL;
	nelma_f = NULL;
}

void nelma_start_png_export()
{
	pcb_hid_expose_ctx_t ctx;

	ctx.view.X1 = 0;
	ctx.view.Y1 = 0;
	ctx.view.X2 = PCB->MaxWidth;
	ctx.view.Y2 = PCB->MaxHeight;

	linewidth = -1;
	lastbrush = (gdImagePtr) ((void *) -1);
	lastcolor = -1;

	pcb_hid_expose_all(&nelma_hid, &ctx);
}

static void nelma_do_export(pcb_hid_attr_val_t * options)
{
	int save_ons[PCB_MAX_LAYER + 2];
	int i, len;
	FILE *nelma_config;
	char *buf;
	time_t t;

	if (!options) {
		nelma_get_export_options(0);
		for (i = 0; i < NUM_OPTIONS; i++) {
			nelma_values[i] = nelma_attribute_list[i].default_val;
		}
		options = nelma_values;
	}
	nelma_basename = options[HA_basename].str_value;
	if (!nelma_basename) {
		nelma_basename = "pcb-out";
	}
	nelma_dpi = options[HA_dpi].int_value;
	if (nelma_dpi < 0) {
		fprintf(stderr, "ERROR:  dpi may not be < 0\n");
		return;
	}
	nelma_copperh = options[HA_copperh].int_value;
	nelma_substrateh = options[HA_substrateh].int_value;
	nelma_substratee = options[HA_substratee].real_value;

	nelma_choose_groups();

	for (i = 0; i < PCB_MAX_LAYERGRP; i++) {
		if (nelma_export_group[i]) {
			char tmp_ln[PCB_PATH_MAX];
			const char *ext = pcb_layer_to_file_name(tmp_ln, -1, pcb_layergrp_flags(PCB, i), PCB_FNS_fixed);

			nelma_cur_group = i;

			nelma_start_png(nelma_basename, ext);

			pcb_hid_save_and_show_layer_ons(save_ons);
			nelma_start_png_export();
			pcb_hid_restore_layer_ons(save_ons);

			nelma_finish_png();
		}
	}

	len = strlen(nelma_basename) + 4;
	buf = (char *) malloc(sizeof(*buf) * len);

	sprintf(buf, "%s.em", nelma_basename);
	nelma_config = pcb_fopen(buf, "w");

	free(buf);

	fprintf(nelma_config, "/* Made with PCB Nelma export HID */");
	t = time(NULL);
	fprintf(nelma_config, "/* %s */", ctime(&t));

	nelma_write_nets(nelma_config);
	nelma_write_objects(nelma_config);
	nelma_write_layers(nelma_config);
	nelma_write_materials(nelma_config);
	nelma_write_space(nelma_config);

	fclose(nelma_config);
}

/* *** PNG export (slightly modified code from PNG export HID) ************* */

static int nelma_set_layer_group(pcb_layergrp_id_t group, pcb_layer_id_t layer, unsigned int flags, int is_empty)
{
	if (flags & PCB_LYT_INVIS)
		return 0;

	if ((flags & PCB_LYT_ANYTHING) == PCB_LYT_SILK)
		return 0;

	is_drill = ((flags & PCB_LYT_PDRILL) || (flags & PCB_LYT_UDRILL));
	is_mask = !!(flags & PCB_LYT_MASK);

	if (is_mask) {
		/* Don't print masks */
		return 0;
	}
	if (is_drill) {
		/*
		 * Print 'holes', so that we can fill gaps in the copper
		 * layer
		 */
		return 1;
	}
	if (group == nelma_cur_group) {
		return 1;
	}
	return 0;
}

static pcb_hid_gc_t nelma_make_gc(void)
{
	pcb_hid_gc_t rv = (pcb_hid_gc_t) malloc(sizeof(struct hid_gc_s));
	rv->me_pointer = &nelma_hid;
	rv->cap = pcb_cap_round;
	rv->width = 1;
	rv->color = (struct color_struct *) malloc(sizeof(*rv->color));
	rv->color->r = rv->color->g = rv->color->b = 0;
	rv->color->c = 0;
	return rv;
}

static void nelma_destroy_gc(pcb_hid_gc_t gc)
{
	free(gc);
}

static void nelma_set_color(pcb_hid_gc_t gc, const char *name)
{
	if (nelma_im == NULL) {
		return;
	}
	if (name == NULL) {
		name = "#ff0000";
	}
	if (!strcmp(name, "drill")) {
		gc->color = black;
		gc->erase = 0;
		return;
	}
	gc->color = black;
	gc->erase = 0;
	return;
}

static void nelma_set_line_cap(pcb_hid_gc_t gc, pcb_cap_style_t style)
{
	gc->cap = style;
}

static void nelma_set_line_width(pcb_hid_gc_t gc, pcb_coord_t width)
{
	gc->width = width;
}

static void nelma_set_draw_xor(pcb_hid_gc_t gc, int xor_)
{
	;
}

static void nelma_set_draw_faded(pcb_hid_gc_t gc, int faded)
{
	gc->faded = faded;
}

static void use_gc(pcb_hid_gc_t gc)
{
	int need_brush = 0;

	if (gc->me_pointer != &nelma_hid) {
		fprintf(stderr, "Fatal: GC from another HID passed to nelma HID\n");
		abort();
	}
	if (linewidth != gc->width) {
		/* Make sure the scaling doesn't erase lines completely */
		/*
		   if (SCALE (gc->width) == 0 && gc->width > 0)
		   gdImageSetThickness (im, 1);
		   else
		 */
		gdImageSetThickness(nelma_im, pcb_to_nelma(gc->width));
		linewidth = gc->width;
		need_brush = 1;
	}
	if (lastbrush != gc->brush || need_brush) {
		static void *bcache = 0;
		pcb_hidval_t bval;
		char name[256];
		char type;
		int r;

		switch (gc->cap) {
		case pcb_cap_round:
			type = 'C';
			r = pcb_to_nelma(gc->width / 2);
			break;
		case pcb_cap_square:
			r = pcb_to_nelma(gc->width);
			type = 'S';
			break;
		default:
			assert(!"unhandled cap");
			type = 'C';
			r = 1;
		}
		sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r);

		if (pcb_hid_cache_color(0, name, &bval, &bcache)) {
			gc->brush = (gdImagePtr) bval.ptr;
		}
		else {
			int bg, fg;
			if (type == 'C')
				gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1);
			else
				gc->brush = gdImageCreate(r + 1, r + 1);
			bg = gdImageColorAllocate(gc->brush, 255, 255, 255);
			fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b);
			gdImageColorTransparent(gc->brush, bg);

			/*
			 * if we shrunk to a radius/box width of zero, then just use
			 * a single pixel to draw with.
			 */
			if (r == 0)
				gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg);
			else {
				if (type == 'C')
					gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg);
				else
					gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg);
			}
			bval.ptr = gc->brush;
			pcb_hid_cache_color(1, name, &bval, &bcache);
		}

		gdImageSetBrush(nelma_im, gc->brush);
		lastbrush = gc->brush;

	}
#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
	if (lastcolor != CBLEND(gc)) {
		if (is_drill || is_mask) {
#ifdef FIXME
			fprintf(f, "%d gray\n", gc->erase ? 0 : 1);
#endif
			lastcolor = 0;
		}
		else {
			double r, g, b;
			r = gc->r;
			g = gc->g;
			b = gc->b;
			if (gc->faded) {
				r = 0.8 * 255 + 0.2 * r;
				g = 0.8 * 255 + 0.2 * g;
				b = 0.8 * 255 + 0.2 * b;
			}
#ifdef FIXME
			if (gc->r == gc->g && gc->g == gc->b)
				fprintf(f, "%g gray\n", r / 255.0);
			else
				fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
#endif
			lastcolor = CBLEND(gc);
		}
	}
}

static void nelma_draw_rect(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2)
{
	use_gc(gc);
	gdImageRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
}

static void nelma_fill_rect(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2)
{
	use_gc(gc);
	gdImageSetThickness(nelma_im, 0);
	linewidth = 0;
	gdImageFilledRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c);
}

static void nelma_draw_line(pcb_hid_gc_t gc, pcb_coord_t x1, pcb_coord_t y1, pcb_coord_t x2, pcb_coord_t y2)
{
	if (x1 == x2 && y1 == y2) {
		pcb_coord_t w = gc->width / 2;
		nelma_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w);
		return;
	}
	use_gc(gc);

	gdImageSetThickness(nelma_im, 0);
	linewidth = 0;
	gdImageLine(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gdBrushed);
}

static void nelma_draw_arc(pcb_hid_gc_t gc, pcb_coord_t cx, pcb_coord_t cy, pcb_coord_t width, pcb_coord_t height, pcb_angle_t start_angle, pcb_angle_t delta_angle)
{
	pcb_angle_t sa, ea;

	/*
	 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
	 * in pcb, 0 degrees is to the left and +90 degrees is down
	 */
	start_angle = 180 - start_angle;
	delta_angle = -delta_angle;
	if (delta_angle > 0) {
		sa = start_angle;
		ea = start_angle + delta_angle;
	}
	else {
		sa = start_angle + delta_angle;
		ea = start_angle;
	}

	/*
	 * make sure we start between 0 and 360 otherwise gd does strange
	 * things
	 */
	sa = pcb_normalize_angle(sa);
	ea = pcb_normalize_angle(ea);

#if 0
	printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea);
	printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
				 (void *)im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c);
#endif
	use_gc(gc);
	gdImageSetThickness(nelma_im, 0);
	linewidth = 0;
	gdImageArc(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
						 pcb_to_nelma(2 * width), pcb_to_nelma(2 * height), sa, ea, gdBrushed);
}

static void nelma_fill_circle(pcb_hid_gc_t gc, pcb_coord_t cx, pcb_coord_t cy, pcb_coord_t radius)
{
	use_gc(gc);

	gdImageSetThickness(nelma_im, 0);
	linewidth = 0;
	gdImageFilledEllipse(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy),
											 pcb_to_nelma(2 * radius), pcb_to_nelma(2 * radius), gc->color->c);

}

static void nelma_fill_polygon_offs(pcb_hid_gc_t gc, int n_coords, pcb_coord_t *x, pcb_coord_t *y, pcb_coord_t dx, pcb_coord_t dy)
{
	int i;
	gdPoint *points;

	points = (gdPoint *) malloc(n_coords * sizeof(gdPoint));
	if (points == NULL) {
		fprintf(stderr, "ERROR:  nelma_fill_polygon():  malloc failed\n");
		exit(1);
	}
	use_gc(gc);
	for (i = 0; i < n_coords; i++) {
		points[i].x = pcb_to_nelma(x[i]+dx);
		points[i].y = pcb_to_nelma(y[i]+dy);
	}
	gdImageSetThickness(nelma_im, 0);
	linewidth = 0;
	gdImageFilledPolygon(nelma_im, points, n_coords, gc->color->c);
	free(points);
}

static void nelma_fill_polygon(pcb_hid_gc_t gc, int n_coords, pcb_coord_t *x, pcb_coord_t *y)
{
	nelma_fill_polygon_offs(gc, n_coords, x, y, 0, 0);
}


static void nelma_calibrate(double xval, double yval)
{
	CRASH("nelma_calibrate");
}

static void nelma_set_crosshair(pcb_coord_t x, pcb_coord_t y, int a)
{
}

static int nelma_usage(const char *topic)
{
	fprintf(stderr, "\nnelma exporter command line arguments:\n\n");
	pcb_hid_usage(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]));
	fprintf(stderr, "\nUsage: pcb-rnd [generic_options] -x nelma [nelma options] foo.pcb\n\n");
	return 0;
}

/* *** Miscellaneous ******************************************************* */

#include "dolists.h"

int pplg_check_ver_export_nelma(int ver_needed) { return 0; }

int pplg_uninit_export_nelma(void)
{
	pcb_hid_remove_attributes_by_cookie(nelma_cookie);
	return 0;
}

int pplg_init_export_nelma(void)
{
	PCB_API_CHK_VER;

	memset(&nelma_hid, 0, sizeof(pcb_hid_t));

	pcb_hid_nogui_init(&nelma_hid);
	pcb_dhlp_draw_helpers_init(&nelma_hid);

	nelma_hid.struct_size = sizeof(pcb_hid_t);
	nelma_hid.name = "nelma";
	nelma_hid.description = "Numerical analysis package export";
	nelma_hid.exporter = 1;

	nelma_hid.get_export_options = nelma_get_export_options;
	nelma_hid.do_export = nelma_do_export;
	nelma_hid.parse_arguments = nelma_parse_arguments;
	nelma_hid.set_layer_group = nelma_set_layer_group;
	nelma_hid.make_gc = nelma_make_gc;
	nelma_hid.destroy_gc = nelma_destroy_gc;
	nelma_hid.set_color = nelma_set_color;
	nelma_hid.set_line_cap = nelma_set_line_cap;
	nelma_hid.set_line_width = nelma_set_line_width;
	nelma_hid.set_draw_xor = nelma_set_draw_xor;
	nelma_hid.set_draw_faded = nelma_set_draw_faded;
	nelma_hid.draw_line = nelma_draw_line;
	nelma_hid.draw_arc = nelma_draw_arc;
	nelma_hid.draw_rect = nelma_draw_rect;
	nelma_hid.fill_circle = nelma_fill_circle;
	nelma_hid.fill_polygon = nelma_fill_polygon;
	nelma_hid.fill_polygon_offs = nelma_fill_polygon_offs;
	nelma_hid.fill_rect = nelma_fill_rect;
	nelma_hid.calibrate = nelma_calibrate;
	nelma_hid.set_crosshair = nelma_set_crosshair;

	nelma_hid.usage = nelma_usage;

	pcb_hid_register_hid(&nelma_hid);
	return 0;
}
