/*
 *                            COPYRIGHT
 *
 *  camv-rnd - electronics-related CAM viewer
 *  Copyright (C) 2019,2023 Tibor 'Igor2' Palinkas
 *
 *  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/camv-rnd
 *    lead developer: http://repo.hu/projects/camv-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include <librnd/config.h>

#include <assert.h>

#include <librnd/core/event.h>
#include <librnd/core/compat_misc.h>

#include "data.h"
#include "obj_any.h"
#include <librnd/core/box.h>
#include "conf_core.h"
#include "event.h"

camv_design_t camv;

char camv_measurement_layer_name[] = "measure";


void camv_layer_init(camv_layer_t *layer)
{
	static rnd_xform_mx_t ident = RND_XFORM_MX_IDENT;

	memset(layer, 0, sizeof(camv_layer_t));
	camv_rtree_init(&layer->objs);
	layer->vis = 1;
	memcpy(layer->mx, ident, sizeof(ident));
}

camv_layer_t *camv_layer_new(void)
{
	camv_layer_t *res = malloc(sizeof(camv_layer_t));
	camv_layer_init(res);
	return res;
}

void camv_group_load_begin(camv_design_t *camv)
{
	camv->grp_load = 1;
	camv->grp_idx_last = -1;
}

void camv_group_load_end(camv_design_t *camv)
{
	camv->grp_load = 0;
}


long camv_layer_append_to_design(camv_design_t *camv, camv_layer_t *layer)
{
	long res = -1;

	assert(layer->parent == NULL);
	if (camv->grp_load) {
		if (camv->grp_idx_last < 0) {
			camv->grp_idx_last = camv->layers.used;
			res = camv->layers.used;
			vtp0_append(&camv->layers, layer);
		}
		else {
			vtp0_insert_len(&camv->layers, camv->grp_idx_last, (void **)&layer, 1);
			res = camv->grp_idx_last;
		}
	}
	else {
		res = camv->layers.used;
		vtp0_append(&camv->layers, layer);
	}
	layer->parent = camv;
	return res;
}

long camv_sublayer_append_after(camv_design_t *camv, camv_layer_t *layer, long idx)
{
	idx++;
	vtp0_insert_len(&camv->layers, idx, (void **)&layer, 1);
	return idx;
}

camv_layer_t *camv_layer_by_name(camv_design_t *camv, const char *name, int alloc)
{
	int n;
	camv_layer_t *ly;

	for(n = 0; n < camv->layers.used; n++) {
		ly = camv->layers.array[n];
		if ((ly->name != NULL) && (strcmp(ly->name, name) == 0))
			return camv->layers.array[n];
	}

	if (!alloc)
		return NULL;

	ly = camv_layer_new();
	camv_layer_append_to_design(camv, ly);
	ly->name = rnd_strdup(name);
	camv_layer_invent_color(camv, ly);
	rnd_event(&camv->hidlib, CAMV_EVENT_LAYERS_CHANGED, NULL);
	return ly;
}


void camv_layer_invent_color(camv_design_t *camv, camv_layer_t *layer)
{
	static const int clrs = sizeof(conf_core.appearance.color.layer) / sizeof(conf_core.appearance.color.layer[0]);
	static int next = 0;
	int orig = next;
	const rnd_color_t *clr;

	do {
		clr = &conf_core.appearance.color.layer[next];
		next++;
		if (next > clrs)
			next = 0;
		if (clr != NULL)
			break;
	} while(next != orig);

	if (clr == NULL) {
		rnd_color_load_str(&layer->color, "#000000");
		rnd_message(RND_MSG_ERROR, "Internal error: camv_layer_invent_color(): no color configured\n");
	}
	else
		layer->color = *clr;
}

void camv_layer_free_fields(camv_layer_t *ly)
{
	void *o;
	long n;
	camv_rtree_it_t it;

	free(ly->name); ly->name = NULL;
	free(ly->short_name); ly->short_name = NULL;

	for(o = camv_rtree_all_first(&it, &ly->objs); o != NULL; o = camv_rtree_all_next(&it))
		camv_obj_free(o);
	camv_rtree_uninit(&ly->objs);

	if (ly->parent != NULL) {
		camv_design_t *camv = ly->parent;
		/* optimization: go backward because the most common case is removing the last layer */
		for(n = camv->layers.used - 1; n >= 0; n--) {
			if (camv->layers.array[n] == ly) {
				vtp0_remove(&camv->layers, n, 1);
				break;
			}
		}
		ly->parent = NULL;
	}
}

void camv_layer_destroy(camv_layer_t *ly)
{
	camv_layer_free_fields(ly);
	free(ly);
}


void camv_design_free_fields(camv_design_t *camv)
{
	long n;

	/* optimization: go backward to avoid memmoves */
	for(n = camv->layers.used - 1; n >= 0; n--)
		camv_layer_destroy(camv->layers.array[n]);
	vtp0_uninit(&camv->layers);
}

int camv_layer_set_vis(camv_design_t *camv, rnd_cardinal_t lid_, int vis, int emit_event)
{
	long lid;

	if (lid_ >= camv->layers.used)
		return -1;

	/* find the main layer */
	for(lid = lid_; (lid > 0) && (((camv_layer_t *)camv->layers.array[lid])->sub); lid--) ;


	/* set main layer */
	((camv_layer_t *)camv->layers.array[lid])->vis = vis;

	/* set all sublayers */
	for(lid++; (lid < camv->layers.used) && (((camv_layer_t *)camv->layers.array[lid])->sub); lid++)
		((camv_layer_t *)camv->layers.array[lid])->vis = vis;

	if (emit_event)
		rnd_event(&camv->hidlib, CAMV_EVENT_LAYERVIS_CHANGED, NULL);
	return 0;
}

int camv_is_empty(camv_design_t *camv)
{
	long lid;

	for(lid = 0; lid < camv->layers.used; lid++)
		if (((camv_layer_t *)camv->layers.array[lid])->objs.size != 0)
			return 0;
	return 1;
}

void camv_data_bbox(camv_design_t *camv)
{
	long lid;

	camv->bbox.x1 = camv->bbox.y1 = RND_COORD_MAX;
	camv->bbox.x2 = camv->bbox.y2 = -RND_COORD_MAX;
	for(lid = 0; lid < camv->layers.used; lid++) {
		camv_layer_t *ly = (camv_layer_t *)camv->layers.array[lid];
		if (ly->objs.size != 0) {
			rnd_box_bump_point((rnd_box_t *)&camv->bbox, ly->objs.bbox.x1, ly->objs.bbox.y1);
			rnd_box_bump_point((rnd_box_t *)&camv->bbox, ly->objs.bbox.x2, ly->objs.bbox.y2);
		}
	}

	if ((camv->bbox.x1 == RND_COORD_MAX) || (camv->bbox.y1 == RND_COORD_MAX) || (camv->bbox.x2 == -RND_COORD_MAX) || (camv->bbox.y2 == -RND_COORD_MAX)) {
		/* empty layer - make it 1*1 mm so it's easier to handle */
		camv->bbox.x1 = camv->bbox.y1 = 0;
		camv->bbox.x2 = camv->bbox.y2 = RND_MM_TO_COORD(1);
	}
}

void camv_layer_select(camv_design_t *camv, int idx)
{
	camv->lysel = idx;
	rnd_event(&camv->hidlib, CAMV_EVENT_LAYER_SELECTED, "i", idx);
}

void camv_data_reverse_layers(camv_design_t *camv, int inhibit_ev)
{
	vtp0_t tmp = {0};
	long n, end, i;

	/* go backward from the end, look for non-sub-layers to copy each
	   "layer group" at the end of tmp */
	end = camv->layers.used;
	for(n = camv->layers.used-1; n >= 0; n--) {
		const camv_layer_t *ly = camv->layers.array[n];
		if (!ly->sub) {
			for(i = n; i < end; i++)
				vtp0_append(&tmp, camv->layers.array[i]);
			end = n;
		}
	}

	vtp0_uninit(&camv->layers);
	memcpy(&camv->layers, &tmp, sizeof(vtp0_t));

	if (!inhibit_ev)
		rnd_event(&camv->hidlib, CAMV_EVENT_LAYERS_CHANGED, NULL);
}

