/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem: design rule checker
 *  Copyright (C) 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund in 2023)
 *
 *  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., 31 Milk Street, # 960789 Boston, MA 02196 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
 */

#include "config.h"

#include <librnd/core/error.h>
#include <librnd/core/actions.h>

#include "actions_csch.h"
#include "drc.h"
#include "event.h"

static const char drc_cookie[] = "libcschem/drc";

csch_drc_abst_ref_t *csch_drc_abst_ref_alloc(void)
{
	return calloc(sizeof(csch_drc_abst_ref_t), 1);
}

csch_drc_abst_ref_t *csch_drc_abst_ref_alloc_append(csch_drc_violation_t *v)
{
	csch_drc_abst_ref_t *r = csch_drc_abst_ref_alloc();
	vtp0_append(&v->ref, r);
	return r;
}

void csch_drc_abst_ref_free(csch_drc_abst_ref_t *ref)
{
	free(ref->net_name);
	free(ref->comp_name);
	free(ref->port_name);
	free(ref->attr_key);
	free(ref);
}

csch_drc_violation_t *csch_drc_violation_alloc(void)
{
	csch_drc_violation_t *v = calloc(sizeof(csch_drc_violation_t), 1);
	return v;
}

csch_drc_violation_t *csch_drc_violation_alloc_append(csch_drc_t *ctx)
{
	csch_drc_violation_t *v = csch_drc_violation_alloc();
	vtp0_append(&ctx->violations, v);
	return v;
}

void csch_drc_violation_free(csch_drc_violation_t *v)
{
	long n;
	for(n = 0; n < v->ref.used; n++)
		csch_drc_abst_ref_free(v->ref.array[n]);
	vtp0_uninit(&v->ref);
	free(v->title);
	free(v->desc);
	free(v);
}


void csch_drc_uninit(csch_drc_t *ctx)
{
	long n;

	ctx->ran = 0;
	for(n = 0; n < ctx->violations.used; n++)
		csch_drc_violation_free(ctx->violations.array[n]);
	vtp0_uninit(&ctx->violations);
}


long csch_drc_project(csch_drc_t *ctx, csch_sheet_t *sheet)
{
	ctx->ran = 0;
	vtp0_init(&ctx->violations);

	rnd_event(&sheet->hidlib, CSCH_EVENT_DRC_RUN, "p", ctx);
	if (ctx->ran == 0)
		rnd_message(RND_MSG_ERROR, "No DRC tests ran. Is any DRC capable plugin compiled? Are they disabled? Are all rules disabled? Maybe need to compile the project first?\n");

	return ctx->violations.used;
}

static void csch_drc_print(csch_drc_t *ctx, FILE *f)
{
	long n, m;
	for(n = 0; n < ctx->violations.used; n++) {
		csch_drc_violation_t *v = ctx->violations.array[n];
		fprintf(f, "%s\n", v->title);
		fprintf(f, " %s\n", v->desc);
		for(m = 0; m < v->ref.used; m++) {
			csch_drc_abst_ref_t *r = v->ref.array[m];
			if (r->net_name != NULL)
				fprintf(f, " net %s\n", r->net_name);
			if (r->comp_name != NULL)
				fprintf(f, " component %s\n", r->comp_name);
			if (r->port_name != NULL)
				fprintf(f, " port %s\n", r->port_name);
			if (r->attr_key != NULL)
				fprintf(f, " attribute %s\n", r->attr_key);
		}
		printf("\n");
	}
}

static void csch_drc_log(csch_drc_t *ctx)
{
	long n, m;
	for(n = 0; n < ctx->violations.used; n++) {
		csch_drc_violation_t *v = ctx->violations.array[n];
		rnd_message(RND_MSG_ERROR, "%s\n", v->title);
		rnd_message(RND_MSG_ERROR, " %s\n", v->desc);
		for(m = 0; m < v->ref.used; m++) {
			csch_drc_abst_ref_t *r = v->ref.array[m];
			if (r->net_name != NULL)
				rnd_message(RND_MSG_ERROR, " net %s\n", r->net_name);
			if (r->comp_name != NULL)
				rnd_message(RND_MSG_ERROR, " component %s\n", r->comp_name);
			if (r->port_name != NULL)
				rnd_message(RND_MSG_ERROR, " port %s\n", r->port_name);
			if (r->attr_key != NULL)
				rnd_message(RND_MSG_ERROR, " attribute %s\n", r->attr_key);
		}
		printf("\n");
	}
}





static const char csch_acts_DRC[] = "DRC([list|print|log])";
static const char csch_acth_DRC[] = "Invoke the DRC check. Results are presented as the argument requests.";
static fgw_error_t csch_act_DRC(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	csch_sheet_t *sheet = CSCH_ACT_SHEET;
	csch_drc_t ctx;
	const char *dlg_type = "log";

	RND_ACT_MAY_CONVARG(1, FGW_STR, DRC, dlg_type = argv[1].val.str);

	csch_drc_project(&ctx, sheet);

	if (strcmp(dlg_type, "print") == 0) {
		if (ctx.violations.used > 0) {
			printf("\n=== DRC violations ===\n");
			csch_drc_print(&ctx, stdout);
			printf("\n");
		}
		else
			printf("(no DRC violations)\n");
		csch_drc_uninit(&ctx);
	}
	else if (strcmp(dlg_type, "log") == 0) {
		if (ctx.violations.used > 0) {
			rnd_message(RND_MSG_ERROR, "\n=== DRC violations ===\n");
			csch_drc_log(&ctx);
			rnd_message(RND_MSG_ERROR, "\n");
		}
		else
			rnd_message(RND_MSG_INFO, "No DRC violations\n");
		csch_drc_uninit(&ctx);
	}
	else {
		rnd_message(RND_MSG_ERROR, "Invalid first argument for DRC()\n");
		RND_ACT_IRES(-1);
	}

	return 0;
}

static rnd_action_t csch_drc_act_list[] = {
	{"DRC", csch_act_DRC, csch_acth_DRC, csch_acts_DRC}
};

void csch_drc_act_init(void)
{
	RND_REGISTER_ACTIONS(csch_drc_act_list, drc_cookie);
}

void csch_drc_act_uninit(void)
{
	rnd_remove_actions_by_cookie(drc_cookie);
}

