/* Branch objects implementation */
/* $Id: branch.c,v 1.5 2003/08/02 21:41:55 pasky Exp $ */

/*
 * Copyright (c) 2003  Petr Baudis.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *   * Neither the name of the author nor the names of the product's
 *     contributors may be used to endorse or promote products
 *     derived from this software without specific prior written
 *     permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include <librev/dataobj.h>
#include <librev/delta.h>
#include <librev/inode.h>
#include <librev/librev.h>
#include <librev/list.h>
#include <librev/revision.h>


struct rev_branch *
rev_branch_init (struct rev_inode *inode, struct rev_revision *parent)
{
	struct rev_branch *obj = calloc (1, sizeof (struct rev_branch));

	if (! obj) return NULL;

	obj->number = -1;

	rev_branch_set_parent (obj, parent);
	rev_branch_set_inode (obj, inode);

	rev_list_init (obj->revs);

	return obj;
}

struct rev_branch *
rev_branch_allocate (struct rev_inode *inode, struct rev_revision *parent,
			int number)
{
	struct rev_branch *obj = calloc (1, sizeof (struct rev_branch));

	if (! obj) return NULL;

	rev_branch_set_number (obj, number);

	rev_branch_set_parent (obj, parent);
	rev_branch_set_inode (obj, inode);

	rev_list_init (obj->revs);

	return obj;
}

void
rev_branch_done (struct rev_branch *obj)
{
	struct rev_revision *rev;

	assert (obj && ! obj->refcount);

	if (obj->udata && obj->userops && obj->userops->done)
		obj->userops->done (obj);
	if (obj->data && obj->dataops && obj->dataops->done)
		obj->dataops->done (obj);

	rev_list_foreach (obj->revs, rev) {
		rev_revision_set_branch(rev, NULL);
	} rev_list_foreach_end;

	rev_branch_set_inode (obj, NULL);
	rev_branch_set_parent (obj, NULL);

	free (obj);
}

struct rev_revision *
rev_branch_find_revision (struct rev_branch *obj, int number)
{
	struct rev_revision *rev;

	assert (obj);

	rev_list_foreach (obj->revs, rev) {
		if (rev_revision_get_number (rev) == number)
			return rev;
	} rev_list_foreach_end;

	return NULL;
}

void
rev_branch_set_parent (struct rev_branch *obj, struct rev_revision *parent)
{
	assert (obj);

	if (obj->parent) {
		rev_list_delete (*obj, offspring);

		if (! parent) {
			/* We just removed the branch from a parent. */
			obj->parent = NULL;
			rev_branch_dissociate (obj);
			return;
		}

	} else {
		if (parent) {
			/* We just added the branch to an inode. */
			rev_branch_associate (obj);
		}
	}

	obj->parent = parent;
	if (! obj->parent) {
		return;
	}

	if (obj->number < 0) {
		rev_list_add_after (parent->offsprings.item, *obj, offspring);
	} else {
		struct rev_list_item *last_branch = &parent->offsprings.item;
		struct rev_branch *branch;

		rev_list_foreachback (parent->offsprings, branch) {
			if (branch->number < obj->number) break;
			last_branch = &branch->offspring;
		} rev_list_foreach_end;

		rev_list_add_before (*last_branch, *obj, offspring);
	}
}

void
rev_branch_set_inode (struct rev_branch *obj, struct rev_inode *inode)
{
	assert (obj);

	rev_branch_associate (obj); /* Make sure @obj won't die yet. */

	if (obj->inode) {
		if (! inode) {
			rev_branch_dissociate (obj);
		}
	} else {
		if (inode) {
			rev_branch_associate (obj);
		}
	}

	obj->inode = inode;

	rev_branch_dissociate (obj);
}
