/* Copyright 2011 Bernhard R. Link <brlink@debian.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * SOFTWARE IN THE PUBLIC INTEREST, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include "config.h"

#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <stdbool.h>

#include "globals.h"
#include "statetable.h"
#include "merge.h"

struct statepair {
	int asn, bsn;
};

static int new_state(int asn, int bsn, bool exclusive, struct statetable **st_p, struct statepair **sp_p) {
	int count = (*st_p == NULL)?0:(*st_p)->count;
	struct statetable *st;
	struct statepair *sp = *sp_p;
	int i;

	for (i = count - 1 ; i >= 0 ; i--) {
		if (sp[i].asn == asn && sp[i].bsn == bsn)
			return i;
	}

	if ((count&31) == 0) {
		st = RESIZEvarstruct(st_p, count + 32, struct statetable, transits);
		sp = RESIZE(sp_p, count + 32, struct statepair);
	} else
		st = *st_p;
	st->transits[count].exclusive = exclusive;
	sp[count].asn = asn; sp[count].bsn = bsn;
	st->count = count + 1;
	return count;
}


struct statetable *merge_statetables(struct statetable *a, struct statetable *b) {
	struct statetable *statetable = NULL;
	struct statepair *statepairs = NULL;
	int processed;

	processed = new_state(0, 0, false, &statetable, &statepairs);
	assert(processed == 0);
	while (processed < statetable->count) {
		int asn = statepairs[processed].asn;
		int bsn = statepairs[processed].bsn;
		int w;

		for (w = 0 ; w <= 255 ; w++) {
			int nasn = (asn < 0) ? asn : a->transits[asn].next[w];
			int nbsn = (bsn < 0) ? bsn : b->transits[bsn].next[w];
			int n;

			if (nasn < -1)
				n = nasn;
			else if (nbsn < -1)
				n = nbsn;
			else if (nasn == -1 && nbsn == -1)
				n = -1;
			else {
				bool aexclusive, bexclusive;
				bool exclusive = false;

				aexclusive = nasn >= 0 &&
					a->transits[nasn].exclusive;
				bexclusive = nbsn >= 0 &&
					b->transits[nbsn].exclusive;
				/* if exactly one is exclusive,
				 * give up on the other */
				if (aexclusive) {
					exclusive = true;
					if (!bexclusive)
						nbsn = -1;
				} else if (bexclusive) {
					exclusive = true;
					nasn = -1;
				}
				n = new_state(nasn, nbsn, exclusive,
						&statetable, &statepairs);
			}
			statetable->transits[processed].next[w] = n;
		}
		processed++;
	}
	return statetable;
}

