/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - eeschema file format support
 *  Copyright (C) 2024..2025 Aron Barath
 *  Copyright (C) 2022..2024 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund in 2024)
 *
 *  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/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */

struct pin_decor
{
	const char* name;
	float shorten;
	int (*render_decor)(read_ctx_t* const ctx, csch_cgrp_t* const pin,
		gsxl_node_t* const src_node, const struct pin_decor* const pin_decor,
		int rot, float x, float y);
};

static const struct pin_decor* eeschema_find_pin_decor(
	const struct pin_decor* decors, const char* const name)
{
	while(decors->name)
	{
		if(strcmp(decors->name, name)==0)
		{
			return decors;
		}

		++decors;
	}

	return NULL;
}

static void eeschema_pin_decor__rot2dirvec(int rot, float* dx, float* dy)
{
	switch(rot)
	{
	case 0:   *dx = +1; *dy = 0;  break;
	case 90:  *dx = 0;  *dy = +1; break;
	case 180: *dx = -1; *dy = 0;  break;
	case 270: *dx = 0;  *dy = -1; break;
	default: abort();
	}
}

static int eeschema_pin_decor__none(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	return 0;
}

static int eeschema_pin_decor__circle(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, float x, float y, float r)
{
	if(!csch_alien_mkarc(&ctx->alien, pin, x, y, r, 0, 360, "term-decor"))
	{
		return -1;
	}

	return 0;
}

static int eeschema_pin_decor__arrow(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, float x, float y, float dx, float dy)
{
	const float len = 0.635; /* 25 mil */

	float nx = -dy;
	float ny = dx;

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x+dx*len,
		/* sy */ y+dy*len,
		/* ey */ x+nx*len,
		/* ey */ y-ny*len,
		"term-decor"))
	{
		return -1;
	}

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x+dx*len,
		/* sy */ y+dy*len,
		/* ey */ x-nx*len,
		/* ey */ y+ny*len,
		"term-decor"))
	{
		return -1;
	}

	return 0;
}

static int eeschema_pin_decor__in_triangle(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, float x, float y, float dx, float dy)
{
	const float len = 1.27; /* 50 mil */

	float nx = -dy;
	float ny = dx;

	/* normal must direct to north or west */
	if(nx<0) { nx = -nx; }
	if(ny>0) { ny = -ny; }

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x,
		/* sy */ y,
		/* ey */ x-dx*len-nx*len,
		/* ey */ y-dy*len-ny*len,
		"term-decor"))
	{
		return -1;
	}

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x-dx*len,
		/* sy */ y-dy*len,
		/* ey */ x-dx*len-nx*len,
		/* ey */ y-dy*len-ny*len,
		"term-decor"))
	{
		return -1;
	}

	return 0;
}

static int eeschema_pin_decor__out_triangle(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, float x, float y, float dx, float dy)
{
	const float len = 1.27; /* 50 mil */

	float nx = -dy;
	float ny = dx;

	/* normal must direct to north or west */
	if(nx<0) { nx = -nx; }
	if(ny>0) { ny = -ny; }

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x,
		/* sy */ y,
		/* ey */ x-dy*len,
		/* ey */ y-dx*len,
		"term-decor"))
	{
		return -1;
	}

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x-dx*len,
		/* sy */ y-dy*len,
		/* ey */ x-nx*len,
		/* ey */ y-ny*len,
		"term-decor"))
	{
		return -1;
	}

	return 0;
}

static int eeschema_pin_decor__cross(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, float x, float y, float size)
{
	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x-size,
		/* sy */ y-size,
		/* ey */ x+size,
		/* ey */ y+size,
		"term-decor"))
	{
		return -1;
	}

	if(!csch_alien_mkline(&ctx->alien, pin,
		/* sx */ x-size,
		/* sy */ y+size,
		/* ey */ x+size,
		/* ey */ y-size,
		"term-decor"))
	{
		return -1;
	}

	return 0;
}

static int eeschema_pin_decor_g_inverted(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy, r;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	r = pin_decor->shorten * 0.5;

	x -= dx * r;
	y -= dy * r;

	return eeschema_pin_decor__circle(ctx, pin, x, y, r);
}

static int eeschema_pin_decor_g_clock(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	return eeschema_pin_decor__arrow(ctx, pin, x, y, dx, dy);
}

static int eeschema_pin_decor_g_inverted_clock(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy, r;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	if(eeschema_pin_decor__arrow(ctx, pin, x, y, dx, dy)!=0)
	{
		return -1;
	}

	r = pin_decor->shorten * 0.5;

	x -= dx * r;
	y -= dy * r;

	return eeschema_pin_decor__circle(ctx, pin, x, y, r);
}

static int eeschema_pin_decor_g_input_low(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	return eeschema_pin_decor__in_triangle(ctx, pin, x, y, dx, dy);
}

static int eeschema_pin_decor_g_clock_low(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	if(eeschema_pin_decor__arrow(ctx, pin, x, y, dx, dy)!=0)
	{
		return -1;
	}

	return eeschema_pin_decor__in_triangle(ctx, pin, x, y, dx, dy);
}

static int eeschema_pin_decor_g_output_low(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	return eeschema_pin_decor__out_triangle(ctx, pin, x, y, dx, dy);
}

static int eeschema_pin_decor_g_edge_clk_high(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	if(eeschema_pin_decor__arrow(ctx, pin, x, y, dx, dy)!=0)
	{
		return -1;
	}

	return eeschema_pin_decor__in_triangle(ctx, pin, x, y, dx, dy);
}

static int eeschema_pin_decor_g_non_logic(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	return eeschema_pin_decor__cross(ctx, pin, x, y, 0.635 /* 25 mil */);
}

static int eeschema_pin_decor_e_no_connect(read_ctx_t* const ctx,
	csch_cgrp_t* const pin, gsxl_node_t* const src_node,
	const struct pin_decor* const pin_decor, int rot, float x, float y)
{
	float dx, dy;

	eeschema_pin_decor__rot2dirvec(rot, &dx, &dy);

	return eeschema_pin_decor__cross(ctx, pin, x, y, 0.3556 /* 14 mil */);
}

/* "base" pin decor is which is (usually) closer to the symbol body */
/* (these are the "graphical" types) */
static const struct pin_decor eeschema_base_pin_decors[] =
{
	{ "line",            0,    eeschema_pin_decor__none },
	{ "inverted",        1.27, eeschema_pin_decor_g_inverted },
	{ "clock",           0,    eeschema_pin_decor_g_clock },
	{ "inverted_clock",  1.27, eeschema_pin_decor_g_inverted_clock },
	{ "input_low",       0,    eeschema_pin_decor_g_input_low },
	{ "clock_low",       0,    eeschema_pin_decor_g_clock_low },
	{ "output_low",      0,    eeschema_pin_decor_g_output_low },
	{ "edge_clock_high", 0,    eeschema_pin_decor_g_edge_clk_high },
	{ "non_logic",       0,    eeschema_pin_decor_g_non_logic },
	{ 0 }
};

/* "end" pin decor is the decoration at the tip of the pin */
/* (these are the "electrical" types) */
static const struct pin_decor eeschema_end_pin_decors[] =
{
	{ "input",          0, eeschema_pin_decor__none },
	{ "output",         0, eeschema_pin_decor__none },
	{ "bidirectional",  0, eeschema_pin_decor__none },
	{ "tri_state",      0, eeschema_pin_decor__none },
	{ "passive",        0, eeschema_pin_decor__none },
	{ "free",           0, eeschema_pin_decor__none },
	{ "power_in",       0, eeschema_pin_decor__none },
	{ "power_out",      0, eeschema_pin_decor__none },
	{ "open_collector", 0, eeschema_pin_decor__none },
	{ "open_emitter",   0, eeschema_pin_decor__none },
	{ "unspecified",    0, eeschema_pin_decor__none },
	{ "no_connect",     0, eeschema_pin_decor_e_no_connect },
	{ 0 }
};
