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

    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
*/

#include <string.h>
#include <math.h>
#include "grbs.h"
#include "route.h"
#include "force_attach.h"

static void grbs_nullprintf(const char *fmt, ...) {}

#include "geo.c"
#include "obj_line.c"
#include "obj_arc.c"
#include "obj_point.c"

void grbs_init(grbs_t *grbs)
{
	memset(grbs, 0, sizeof(grbs_t));

	grbs_rtree_init(&grbs->line_tree);
	grbs_rtree_init(&grbs->arc_tree);
	grbs_rtree_init(&grbs->point_tree);

	/* initialize the low level allocator: use libc's malloc/free */
	grbs->sys.alloc     = uall_stdlib_alloc;
	grbs->sys.free      = uall_stdlib_free;
	grbs->sys.page_size = 4096; /* how much memory to allocate to grow the stack */

	/* initialize the stack allocators */
	grbs->stk_points.sys = &grbs->sys;  grbs->stk_points.elem_size = sizeof(grbs_point_t);
	grbs->stk_2nets.sys = &grbs->sys;   grbs->stk_2nets.elem_size = sizeof(grbs_2net_t);
	grbs->stk_arcs.sys = &grbs->sys;    grbs->stk_arcs.elem_size = sizeof(grbs_arc_t);
	grbs->stk_lines.sys = &grbs->sys;   grbs->stk_lines.elem_size = sizeof(grbs_line_t);
	grbs->stk_addrs.sys = &grbs->sys;   grbs->stk_addrs.elem_size = sizeof(grbs_addr_t);
}

void grbs_uninit(grbs_t *grbs)
{
	grbs_rtree_uninit(&grbs->line_tree);
	grbs_rtree_uninit(&grbs->arc_tree);
	grbs_rtree_uninit(&grbs->point_tree);

	uall_stacks_clean(&grbs->stk_points);
	uall_stacks_clean(&grbs->stk_2nets);
	uall_stacks_clean(&grbs->stk_arcs);
	uall_stacks_clean(&grbs->stk_lines);
	uall_stacks_clean(&grbs->stk_addrs);

	vtp0_uninit(&grbs->collobjs);
}

/* Low level object allocation: try to reuse an object from the free list,
   if that's not possible, allocate a new one on the stack */
#define grbs_new_(grbs, type, var, stk, all_lst, free_lst, linkfld) \
	do { \
		if (!grbs->force_new_alloc) \
			var = gdl_first(&grbs->free_lst); \
		else \
			var = NULL; \
		if (var == NULL) \
			var = uall_stacks_alloc(&grbs->stk); \
		else \
			gdl_remove(&grbs->free_lst, var, linkfld); \
		memset(var, 0, sizeof(type)); \
		var->uid = ++grbs->uids; \
		gdl_append(&grbs->all_lst, var, linkfld); \
	} while(0)

/* Allocate a new object; "what" is arc, point or 2net; result is in var */
#define grbs_new(grbs, what, var) \
	grbs_new_(grbs, grbs_ ## what ##_t, var, stk_ ## what ## s, all_ ## what ## s, free_ ## what ## s, link_ ## what ## s)


#define grbs_free_(grbs, var, all_lst, free_lst, linkfld) \
	do { \
		gdl_remove(&grbs->all_lst, var, linkfld); \
		gdl_append(&grbs->free_lst, var, linkfld); \
	} while(0)

#define grbs_free(grbs, what, var) \
	grbs_free_(grbs, var, all_ ## what ## s, free_ ## what ## s, link_ ## what ## s)


grbs_point_t *grbs_point_new(grbs_t *grbs, double x, double y, double copper, double clearance)
{
	grbs_point_t *p;

	grbs_new(grbs, point, p);

	p->x = x;
	p->y = y;
	p->copper = copper;
	p->clearance = clearance;

	grbs_point_reg(grbs, p);

	return p;
}

void grbs_point_free(grbs_t *grbs, grbs_point_t *p)
{
	grbs_point_unreg(grbs, p);
	grbs_free(grbs, point, p);
}

grbs_arc_t *grbs_arc_new(grbs_t *grbs, grbs_point_t *parent, int seg, double r, double sa, double da)
{
	grbs_arc_t *a, *n;

	grbs_new(grbs, arc, a);

	a->r  = r;
	a->sa = sa;
	a->da = da;
	a->parent_pt = parent;
	a->segi = seg;

	if (r != 0) {
		for(n = gdl_first(&parent->arcs[seg]); n != NULL; n = gdl_next(&parent->arcs[seg], n))
			if (n->r >= r)
				break;
		if (n != NULL)
			gdl_insert_before(&parent->arcs[seg], n, a, link_point);
		else
			gdl_append(&parent->arcs[seg], a, link_point);
	}
	else
		gdl_append(&parent->incs, a, link_point);

	return a;
}

void grbs_arc_free(grbs_t *grbs, grbs_arc_t *a)
{
	/* require the caller to remove the arc before free'ing it */
	assert(a->link_point.parent == NULL);
	assert(a->link_2net.parent == NULL);

	grbs_free(grbs, arc, a);
}


grbs_line_t *grbs_line_new(grbs_t *grbs)
{
	grbs_line_t *l;

	grbs_new(grbs, line, l);

	return l;
}

void grbs_line_free(grbs_t *grbs, grbs_line_t *l)
{
	grbs_free(grbs, line, l);
}


grbs_2net_t *grbs_2net_new(grbs_t *grbs, double copper, double clearance)
{
	grbs_2net_t *tn;

	grbs_new(grbs, 2net, tn);

	tn->copper    = copper;
	tn->clearance = clearance;

	return tn;
}

void grbs_2net_free(grbs_t *grbs, grbs_2net_t *tn)
{
	grbs_free(grbs, 2net, tn);
}


grbs_addr_t *grbs_addr_new(grbs_t *grbs, grbs_addr_type_t type, void *obj)
{
	grbs_addr_t *a;

	a = uall_stacks_alloc(&grbs->stk_addrs); /* this one doesn't have a free/all list as its temporary allocated for A* */
	a->type   = type;
	a->obj.pt = obj;

	return a;
}

void grbs_addr_free_last(grbs_t *grbs)
{
	uall_stacks_free(&grbs->stk_addrs);
}


#include "addr.c"
#include "collision.c"
#include "route_common.c"
#include "route_next.c"
#include "route_realize.c"
#include "route_remove.c"
#include "route_detach.c"
#include "force_attach.c"
#include "route_modify.c"
#include "route_sanity.c"
