/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design - Rubber Band Stretch Router
 *  Copyright (C) 2024,2025 Tibor 'Igor2' Palinkas
 *  (Supported by NLnet NGI0 Entrust in 2024 amd 2025)
 *
 *  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/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include <libgrbs/route.h>
#include "install.h"
#include "route_helper.h"


static int seq_coll_ignore_tn_point(grbs_t *grbs, grbs_2net_t *tn, grbs_point_t *p)
{
	rbsr_seq_t *rbsq = grbs->user_data;
	grbs_point_t *pst;
	

	/* SMD: we are routing to the center point, incident, ignore any colloision
	   with corner points of the same terminal */
	if ((rbsq->target_incident_term != NULL) && (p->user_data == rbsq->target_incident_term))
		return 1;

	/* SMD: we are routing from the center point, ignore same-terminal corners
	   so the line can leave; technically this allows collisions with later
	   sections too, but that's a same-net collision anyway */
	pst = rbsq->path[0].pt;
	if (p->user_data == pst->user_data)
		return 1;

	return 0;
}

int rbsr_seq_begin_at(rbsr_seq_t *rbsq, pcb_board_t *pcb, rnd_layer_id_t lid, rnd_coord_t tx, rnd_coord_t ty, rnd_coord_t copper, rnd_coord_t clearance)
{
	grbs_point_t *start;

	if ((pcb_layer_flags(pcb, lid) & PCB_LYT_COPPER) == 0) {
		rnd_message(RND_MSG_ERROR, "Please activate a copper layer first!\n");
		return -1;
	}

	if (rbsr_map_pcb(&rbsq->map, pcb, lid) != 0) {
		rnd_message(RND_MSG_ERROR, "Failed to map layer for grbs\n");
		rbsr_map_uninit(&rbsq->map);
		return -1;
	}

	rbsr_map_debug_draw(&rbsq->map, "rbsq1.svg");
	rbsr_map_debug_dump(&rbsq->map, "rbsq1.dump");
	rbsr_map_debug_save_test(&rbsq->map, "rbsq1.grbs");

	rbsq->map.grbs.user_data = rbsq;
#if 0
	rbsq->map.grbs.coll_ingore_tn_line = coll_ingore_tn_line;
	rbsq->map.grbs.coll_ingore_tn_arc = coll_ingore_tn_arc;
#endif
	rbsq->map.grbs.coll_ingore_tn_point = seq_coll_ignore_tn_point;

	start = rbsr_find_point_by_center(&rbsq->map, tx, ty);
	if (start == NULL) start = rbsr_find_point_thick(&rbsq->map, tx, ty, RND_MM_TO_COORD(0.5));
	if (start == NULL) start = rbsr_find_point_thick(&rbsq->map, tx, ty, RND_MM_TO_COORD(1));
	if (start == NULL) start = rbsr_find_point_thick(&rbsq->map, tx, ty, RND_MM_TO_COORD(2));
	if (start == NULL) start = rbsr_find_point_thick(&rbsq->map, tx, ty, RND_MM_TO_COORD(4));
	if (start == NULL) {
		rnd_message(RND_MSG_ERROR, "No suitable starting point\n");
		rbsr_map_uninit(&rbsq->map);
		return -1;
	}

	rbsq->tn = grbs_2net_new(&rbsq->map.grbs, RBSR_R2G(copper), RBSR_R2G(clearance));

	rbsq->snap = grbs_snapshot_save(&rbsq->map.grbs);
	rbsq->map.grbs.force_new_alloc = 1; /* don't reuse old (deleted from grbs model) objects with ->user_data loaded; important for snapshot comparison based install() */

	rbsq->last_x = RBSR_G2R(start->x);
	rbsq->last_y = RBSR_G2R(start->y);

	rbsq->path[0].dir = GRBS_ADIR_INC;
	rbsq->path[0].pt = start;
	rbsq->used = 1;

	rbsr_ui_save(&rbsq->map);

	return 0;
}

void seq_coll_arc_cb(grbs_t *grbs, grbs_2net_t *tn, grbs_2net_t *coll_tn, grbs_arc_t *coll_arc)
{
/*	rbsr_seq_t *rbsq = grbs->user_data;*/
	coll_arc->RBSR_COLL_FLAG = 1;
}

void seq_coll_line_cb(grbs_t *grbs, grbs_2net_t *tn, grbs_2net_t *coll_tn, grbs_line_t *coll_line)
{
/*	rbsr_seq_t *rbsq = grbs->user_data;*/
	coll_line->RBSR_COLL_FLAG = 1;
}

void seq_coll_pt_cb(grbs_t *grbs, grbs_2net_t *tn, grbs_point_t *coll_pt)
{
/*	rbsr_seq_t *rbsq = grbs->user_data;*/
	coll_pt->RBSR_COLL_FLAG = 1;
}


RND_INLINE int rbsr_seq_redraw(rbsr_seq_t *rbsq)
{
	grbs_t *grbs = &rbsq->map.grbs;
	grbs_addr_t *last, *curr = NULL, *cons = NULL;
	int n, broken = 0, res = 0;

	grbs_path_remove_2net_addrs(grbs, rbsq->tn);
	grbs_snapshot_restore(rbsq->snap);

	rnd_trace("-- route path\n");
	last = grbs_addr_new(grbs, ADDR_POINT, rbsq->path[0].pt);
	last->last_real = NULL;
	rnd_trace(" strt=%p\n", last);

	for(n = 1; n < rbsq->used; n++) {
		curr = grbs_path_next(grbs, rbsq->tn, last, rbsq->path[n].pt, rbsq->path[n].dir);
		rnd_trace(" curr=%p\n", curr);
		if (curr == NULL) {
			curr = last;
			broken = 1;
			break;
		}
		last = curr;
	}

	if (!broken && (rbsq->consider.dir != RBS_ADIR_invalid)) {
		grbs->coll_report_arc_cb = seq_coll_arc_cb;
		grbs->coll_report_line_cb = seq_coll_line_cb;
		grbs->coll_report_pt_cb = seq_coll_pt_cb;

		cons = grbs_path_next(grbs, rbsq->tn, last, rbsq->consider.pt, rbsq->consider.dir);
		if (cons != NULL)
			curr = cons;
		else
			res = -1;

		grbs->coll_report_arc_cb = NULL;
		grbs->coll_report_line_cb = NULL;
		grbs->coll_report_pt_cb = NULL;

		rnd_trace(" cons=%p\n", cons);
	}

	if (curr != NULL) {
		double ex, ey;

		if ((curr->type & 0x0F) == ADDR_POINT) {
			ex = curr->obj.pt->x;
			ey = curr->obj.pt->y;
		}
		else {
			grbs_arc_t *arc = curr->obj.arc;
			if (arc->new_in_use) {
				ex = arc->parent_pt->x + cos(arc->new_sa + arc->new_da) * arc->new_r;
				ey = arc->parent_pt->y + sin(arc->new_sa + arc->new_da) * arc->new_r;
			}
			else {
				ex = arc->parent_pt->x + cos(arc->sa + arc->da) * arc->r;
				ey = arc->parent_pt->y + sin(arc->sa + arc->da) * arc->r;
			}
		}
		rbsq->rlast_x = RBSR_G2R(ex);
		rbsq->rlast_y = RBSR_G2R(ey);
	}

	rnd_trace("realize:\n");
	for(; curr != NULL; curr = curr->last_real) {
		rnd_trace(" r %p\n", curr);
		grbs_path_realize(grbs, rbsq->tn, curr, 0);
	}
	rnd_trace("--\n");

	/* turn the last section into wireframe */
	if (cons != NULL) {
		grbs_arc_t *arc = gdl_first(&rbsq->tn->arcs);
		if (arc != NULL) {
			arc->RBSR_WIREFRAME_FLAG = 1;
			if (arc->da == 0) {
				/* make the consider-arc non-zero in length to make it visible */
				if (rbsq->consider.dir == GRBS_ADIR_CONVEX_CW)
					arc->da = 1;
				else if (rbsq->consider.dir == GRBS_ADIR_CONVEX_CCW)
					arc->da = -1;
			}
			if (arc->eline != NULL) {
				arc->eline->RBSR_WIREFRAME_FLAG = 1;
				arc = gdl_next(&rbsq->tn->arcs, arc);
				if (arc != NULL)
					arc->RBSR_WIREFRAME_FLAG = 1;
			}
		}
	}

	return res;
}

int rbsr_seq_consider(rbsr_seq_t *rbsq, rnd_coord_t tx, rnd_coord_t ty, int *need_redraw_out)
{
	grbs_point_t *end;
	grbs_arc_dir_t dir;
	int refuse_incident = 0;

	end = rbsr_crosshair_get_pt(&rbsq->map, tx, ty, 1, &refuse_incident);
	if (end == NULL) {
		refuse: {
		int need_redraw = 0;
		if (rbsq->consider.dir != RBS_ADIR_invalid)
			need_redraw = 1;

		rbsq->consider.dir = RBS_ADIR_invalid;
		if (need_redraw)
			rbsr_seq_redraw(rbsq);
		*need_redraw_out = need_redraw;
		return -1;
		}
	}

	/* allow collision with any other point in the same terminal when going incident */
	rbsq->target_incident_term = end->user_data;

	dir = rbsr_crosshair_get_pt_dir(&rbsq->map, rbsq->last_x, rbsq->last_y, tx, ty, end);
	if (dir == GRBS_ADIR_INC) {
		if (refuse_incident)
			goto refuse;
		rnd_trace(" incident\n");
	}

	if ((rbsq->consider.pt == end) && (rbsq->consider.dir == dir)) {
		*need_redraw_out = 0;
		return 0; /* do not redraw if there's no change */
	}

	if ((rbsq->used > 2) && (end == rbsq->path[rbsq->used-2].pt)) {
		int res;
		grbs_arc_t *arc;

		rnd_trace("jajj ------------------------------!\n");
		rbsq->consider.dir = RBS_ADIR_invalid;
		
		
		*need_redraw_out = 1;
		res = rbsr_seq_redraw(rbsq);

		arc = gdl_first(&rbsq->tn->arcs);
		if (arc != NULL) {
			arc->RBSR_WIREFRAME_FLAG = 1;
			if (arc->eline != NULL)
				arc->eline->RBSR_WIREFRAME_FLAG = 1;
		}

		rbsq->consider_step_back = 1;
		return res;
	}
	rbsq->consider_step_back = 0;

	rbsq->consider.pt = end;
	rbsq->consider.dir = dir;

	*need_redraw_out = 1;
	return rbsr_seq_redraw(rbsq);
}

rbsr_seq_accept_t rbsr_seq_accept(rbsr_seq_t *rbsq)
{
	rbsr_seq_accept_t res = RBSR_SQA_CONTINUE;

	if (rbsq->consider_step_back) {
		rbsr_seq_step_back(rbsq);
		rbsq->consider_step_back = 0;
		return res;
	}

	if (rbsq->used >= RBSR_SEQ_MAX) {
		rnd_message(RND_MSG_ERROR, "rbsr_seq_accept(): path too long\n");
		return res;
	}

	rbsq->path[rbsq->used] = rbsq->consider;
	rbsq->used++;
	rbsq->last_x = rbsq->rlast_x;
	rbsq->last_y = rbsq->rlast_y;

	if (rbsq->consider.dir == GRBS_ADIR_INC) {
		res = RBSR_SQA_TERMINATE;
		rbsq->consider.dir = 0;
	}

	rbsr_seq_redraw(rbsq);
	return res;
}

void rbsr_seq_step_back(rbsr_seq_t *rbsq)
{
	grbs_point_t *start;

	if (rbsq->used <= 1)
		return;

	/* fallback for undoing the first seg., when rbsr_seq_redraw() won't fill
	   it in: revert to start from the starting obj */
	start = rbsq->path[0].pt;
	rbsq->rlast_x = RBSR_G2R(start->x);
	rbsq->rlast_y = RBSR_G2R(start->y);

	/* remove the last segment from path and reset "consider" */
	rbsq->used--;
	rbsq->consider.dir = RBS_ADIR_invalid;
	rbsr_seq_redraw(rbsq);

	/* update routing-from coords for the tool code */
	rbsq->last_x = rbsq->rlast_x;
	rbsq->last_y = rbsq->rlast_y;
}

void rbsr_seq_end(rbsr_seq_t *rbsq)
{
	/* tune existing objects and install new objects */
	if (rbsq->used > 0)
		rbsr_install_by_snapshot(&rbsq->map, pcb_get_layer(rbsq->map.pcb->Data, rbsq->map.lid), rbsq->snap);

	rbsr_ui_restore(&rbsq->map);

	grbs_snapshot_free(rbsq->snap);
	rbsq->snap = NULL;

	rbsr_map_uninit(&rbsq->map);
}

