/*  libgrbs - geometric rubber band sketch model
    Copyright (C) 2025  Tibor 'Igor2' Palinkas
    (Supported by NLnet NGI0 Entrust in 2025)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA

    Contact:
      Project page: http://repo.hu/projects/libgrbs
      lead developer: http://repo.hu/projects/pcb-rnd/contact.html
*/

#define TRACE 1
#ifdef TRACE
#include <stdio.h>
#endif

#define ANGLE_TOLERANCE 0.0001

/* verify (or fix) arc angles using the bicycle angle model */
static int grbs_arc_angle_sanity(grbs_t *grbs, grbs_arc_t *arc, int dry)
{
	grbs_arc_t *next = arc->link_2net.next;
	double a[4], arca, nexta;
	int r, crossbelt = 0, ai, ni, changed = 0;

	/* check only current-next relations so each line (and arc endpoint pair)
	   is checked once */
	if (next == NULL)
		return 0;

	if (((arc->da > 0) && (next->da < 0)) || ((arc->da < 0) && (next->da > 0)))
		crossbelt = 1;

	r = grbs_bicycle_angles(
		arc->parent_pt->x, arc->parent_pt->y, arc->r,
		next->parent_pt->x, next->parent_pt->y, next->r, a, crossbelt);

	if (r != 0) {
		fprintf(stderr, "VERIFY fail: bicycle from %.2f %.2f %.2f to %.2f %.2f %.2f\n", arc->parent_pt->x, arc->parent_pt->y, arc->r, next->parent_pt->x, next->parent_pt->y, next->r);
		return 1;
	}

	arca = arc->sa + arc->da;
	if (arca >= 2*GRBS_PI)
		arca -= 2*GRBS_PI;
	else if (arca < 0)
		arca += 2*GRBS_PI;

	nexta = next->sa;
	if (nexta >= 2*GRBS_PI)
		nexta -= 2*GRBS_PI;
	else if (nexta < 0)
		nexta += 2*GRBS_PI;

	grbs_gen_bicycle_idx(arc, next, crossbelt, 1, &ai, &ni);

#ifdef TRACE
	if (arc->r != 0)
		fprintf(stderr, "VERIFY 1: %.2f %.2f: %f vs. %f (%f %f) ai=%d cr=%d\n", arc->parent_pt->x, arc->parent_pt->y, arca, a[ai], a[0], a[1], ai, crossbelt);
	else
		fprintf(stderr, "VERIFY 1: *\n");

	if (next->r != 0)
		fprintf(stderr, "VERIFY 2: %.2f %.2f: %f vs. %f (%f %f) ni=%d cr=%d\n", next->parent_pt->x, next->parent_pt->y, nexta, a[ni], a[2], a[3], ni, crossbelt);
	else
		fprintf(stderr, "VERIFY 2: *\n");
#endif

	if (dry) {
		if (arc->r != 0) {
			if (fabs(arca - a[ai]) > ANGLE_TOLERANCE)
				return 1;
		}
		if (next->r != 0) {
			if (fabs(nexta - a[ni]) > ANGLE_TOLERANCE)
				return 1;
		}
	}
	else {
		if (arc->r != 0) {
			if (fabs(arca - a[ai]) > ANGLE_TOLERANCE) {
				double orig_da = arc->da;

				arc->da = a[ai] - arc->sa;
				changed = 1;

				/* make sure da sign does not change */
				if ((arc->da < 0) && (orig_da > 0))
					arc->da += 2*GRBS_PI;
				else if ((arc->da > 0) && (orig_da < 0))
					arc->da -= 2*GRBS_PI;
			}
		}
		if (next->r != 0) {
			if (fabs(nexta - a[ni]) > ANGLE_TOLERANCE) {
				next->sa = a[ni];
				changed = 1;
			}
		}
	}

	if (changed)
		auto_update_seg_sentinel_angles(arc);

#ifdef TRACE
	if (changed)
		fprintf(stderr, "  -> fixed sa=%f da=%f\n", arc->sa, arc->da);
	fprintf(stderr, "\n");
#endif

	return 0;
}

int grbs_sanity(grbs_t *grbs, int dry)
{
	grbs_arc_t *a;
	int res = 0;

	for(a = gdl_first(&grbs->all_arcs); a != NULL; a = gdl_next(&grbs->all_arcs, a))
		res |= grbs_arc_angle_sanity(grbs, a, dry);

	return res;
}
