/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  Copyright (C) 2018 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/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

#include "config.h"

#include "data.h"
#include "layer.h"
#include "obj_subc.h"
#include "obj_arc.h"
#include "obj_line.h"
#include "obj_text.h"
#include "obj_poly.h"
#include "obj_pstk.h"

int pcb_subc_hash_ignore_uid = 0;

int pcb_subc_eq(pcb_subc_t *sc1, pcb_subc_t *sc2)
{
	pcb_host_trans_t tr1, tr2;
	int lid;
	gdl_iterator_t it;

	/* optimization: cheap tests first */
	if (!pcb_subc_hash_ignore_uid && memcmp(&sc1->uid, &sc2->uid, sizeof(sc1->uid)) != 0) return 0;
	if (sc1->data->LayerN != sc2->data->LayerN) return 0;
	if (padstacklist_length(&sc1->data->padstack) != padstacklist_length(&sc2->data->padstack)) return 0;
	if (pcb_subclist_length(&sc1->data->subc) != pcb_subclist_length(&sc2->data->subc)) return 0;

	for(lid = 0; lid < sc1->data->LayerN; lid++) {
		pcb_layer_t *ly1 = &sc1->data->Layer[lid];
		pcb_layer_t *ly2 = &sc2->data->Layer[lid];
TODO("todo")
/*		if (!pcb_layer_eq_bound(ly1, tr1.on_bottom, ly2, tr2.on_bottom)) return 0;*/
		if (arclist_length(&ly1->Arc) != arclist_length(&ly2->Arc)) return 0;
	}

	pcb_subc_get_host_trans(sc1, &tr1, 1);
	pcb_subc_get_host_trans(sc2, &tr2, 1);

	for(lid = 0; lid < sc1->data->LayerN; lid++) {
		pcb_line_t *l1, *l2;
		pcb_arc_t *a1, *a2;
		pcb_text_t *t1, *t2;
		pcb_poly_t *p1, *p2;
		pcb_layer_t *ly1 = &sc1->data->Layer[lid];
		pcb_layer_t *ly2 = &sc2->data->Layer[lid];


		/* assume order of children objects are the same */
		a2 = arclist_first(&ly2->Arc);
		arclist_foreach(&ly1->Arc, &it, a1) {
			if (!pcb_arc_eq(&tr1, a1, &tr2, a2)) return 0;
			a2 = arclist_next(a2);
		}

		l2 = linelist_first(&ly2->Line);
		linelist_foreach(&ly1->Line, &it, l1) {
			if (!pcb_line_eq(&tr1, l1, &tr2, l2)) return 0;
			l2 = linelist_next(l2);
		}

		t2 = textlist_first(&ly2->Text);
		textlist_foreach(&ly1->Text, &it, t1) {
			if (!pcb_text_eq(&tr1, t1, &tr2, t2)) return 0;
			t2 = textlist_next(t2);
		}

		p2 = polylist_first(&ly2->Polygon);
		polylist_foreach(&ly1->Polygon, &it, p1) {
			if (!pcb_poly_eq(&tr1, p1, &tr2, p2)) return 0;
			p2 = polylist_next(p2);
		}
	}

	/* compare global objects */
	{
		pcb_pstk_t *p1, *p2;

		p2 = padstacklist_first(&sc2->data->padstack);
		padstacklist_foreach(&sc1->data->padstack, &it, p1) {
			if (!pcb_pstk_eq(&tr1, p1, &tr2, p2)) return 0;
			p2 = padstacklist_next(p2);
		}
TODO("subc: subc-in-subc eq check")
	}

	return 1;
}

unsigned int pcb_subc_hash(pcb_subc_t *sc)
{
	unsigned int hash;
	int lid;
	pcb_host_trans_t tr;
	gdl_iterator_t it;

	pcb_subc_get_host_trans(sc, &tr, 1);

	hash = sc->data->LayerN;

	if (!pcb_subc_hash_ignore_uid)
		hash ^= murmurhash(&sc->uid, sizeof(sc->uid));

	/* hash layers and layer objects */

	for(lid = 0; lid < sc->data->LayerN; lid++) {
		pcb_layer_t *ly = &sc->data->Layer[lid];

		pcb_line_t *l;
		pcb_arc_t *a;
		pcb_text_t *t;
		pcb_poly_t *p;

		hash ^= pcb_layer_hash_bound(ly, tr.on_bottom);

		arclist_foreach(&ly->Arc, &it, a)
			hash ^= pcb_arc_hash(&tr, a);

		linelist_foreach(&ly->Line, &it, l)
			hash ^= pcb_line_hash(&tr, l);

		textlist_foreach(&ly->Text, &it, t)
			hash ^= pcb_text_hash(&tr, t);

		polylist_foreach(&ly->Polygon, &it, p)
			hash ^= pcb_poly_hash(&tr, p);
	}

	/* hash global objects */
	{
		pcb_pstk_t *ps;
TODO("subc: subc-in-subc: trans in trans")
#if 0
		pcb_subc_t *s;
		subclist_foreach(&sc->data->subc, &it, s)
			hash ^= pcb_subc_hash_(&tr, p);
#endif
		padstacklist_foreach(&sc->data->padstack, &it, ps)
			hash ^= pcb_pstk_hash(&tr, ps);
	}
	return hash;
}

