#ifndef LIBREV__REVISION_H
#define LIBREV__REVISION_H

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

#ifdef __cplusplus
extern "C" {
#endif


#include <librev/branch.h>
#include <librev/dataobj.h>
#include <librev/datetime.h>
#include <librev/delta.h>
#include <librev/librev.h>
#include <librev/list.h>

#include <time.h>


/* This header file contains the revision object ({struct rev_revision}).  This
 * object is used as a basic unit of inode history, holding a set of deltas to
 * the neighbour revisions and possibly a dataset describing the state of the
 * inode as in this revision. */


struct rev_branch;

rev_object (revision,
	/* Public attributes */


	/* DAG linking */

	/* Ordered linked list of revisions in the branch. */
	struct rev_list_item rev; /* rev_branch.revs */

	/* The branch this revision belongs to. Note that the branch is
	 * superior object to revision, thus we don't increment branch's
	 * refcount. A revision always gets notified when branch is going
	 * away. */
	/* {rev_revision_get_branch()},{rev_revision_set_branch()} */
	struct rev_branch *branch;

	/* Ordered linked list of offsprings, that is branches forked out
	 * of this revision. */
	struct rev_list_head offsprings; /* rev_branch.offspring */


	/* The data snapshots */
	/* At least one of these should be non-NULL. */

	/* The snapshot of data at this revision (may be NULL). */
	/* {rev_revision_get_dataobj()},{rev_revision_set_dataobj()} */
	struct rev_dataobj *dataobj;

	/* The deltas to surrounding revisions (may be NULL). */
	/* {rev_revision_get_delta_forw()},{rev_revision_set_delta_forw()} */
	struct rev_delta *delta_forw; /* D(R-1,R) (RCS branch style) */
	/* {rev_revision_get_delta_rev()},{rev_revision_set_delta_rev()} */
	struct rev_delta *delta_rev; /* D(R+1,R) (RCS trunk style) */


	/* Checkin information */

	/* Number of revision (counter from 1) in the branch. */
	/* {rev_revision_get_number()},{rev_revision_set_number()} */
	int number;

	/* Checkin time. */
	/* {rev_revision_get_ctime()},{rev_revision_set_ctime()} */
	struct rev_datetime *ctime;

	/* Checkin done by... */
	/* {rev_revision_get_author()},{rev_revision_set_author()} */
	char *author;

	/* Checkin comment. */
	/* {rev_revision_get_comment()},{rev_revision_set_comment()} */
	char *comment;


	/* Backend data: */

	void *data; /* Backend's data. */

	struct rev_revision_dataops {
		/* Release obj->data. */
		void (*done) (struct rev_revision *obj);
	} *dataops;


	/* User data: */

	void *udata; /* User's data. */

	struct rev_revision_userops {
		/* Release obj->udata. */
		void (*done) (struct rev_revision *obj);
	} *userops;
);

/* Constructor */

/* This creates the revision. Most of the data must be filled in manually
 * later. */
/* Returns the pointer on success, NULL otherwise. */
struct rev_revision *rev_revision_init (struct rev_branch *branch);

/* This creates the revision and inserts it into a proper slot in the
 * revisions list. */
/* Returns the pointer on success, NULL otherwise. */
struct rev_revision *rev_revision_allocate (struct rev_branch *branch,
						int number);

/* Actions */

/* This looks up a branch of given number. */
/* Returns the branch structure pointer on success, NULL otherwise. */
struct rev_branch *rev_revision_find_branch (struct rev_revision *obj,
					      int number);

/* This propagates a dataobj through the whole DAG, making sure that each
 * revision will have dataobj set. The propagation will start at the @obj
 * DAG point and the other dataobjs will be derived from appropriate deltas.
 * Note that this task can be quite time-consuming. */
/* Returns 1 on success, 0 if not enough information, -1 otherwise. */
int rev_revision_propagate_dataobj (struct rev_revision *obj);

/* Attributes */

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

/* This returns the branch. */
static inline struct rev_branch *
rev_revision_get_branch (struct rev_revision *obj)
{
	assert (obj);
	return obj->branch;
}

/* This sets the branch. */
void rev_revision_set_branch (struct rev_revision *obj, struct rev_branch *branch);

/* This returns the dataobj. */
static inline struct rev_dataobj *
rev_revision_get_dataobj (struct rev_revision *obj)
{
	assert (obj);
	return obj->dataobj;
}

/* This sets the dataobj. */
static inline void
rev_revision_set_dataobj (struct rev_revision *obj, struct rev_dataobj *dataobj)
{
	assert (obj);
	if (obj->dataobj) {
		rev_dataobj_dissociate (obj->dataobj);
		obj->dataobj = NULL;
	}
	if (dataobj) {
		rev_dataobj_associate (dataobj);
		obj->dataobj = dataobj;
	}
}

/* This returns the forward delta. */
static inline struct rev_delta *
rev_revision_get_delta_forw (struct rev_revision *obj)
{
	assert (obj);
	return obj->delta_forw;
}

/* This sets the forward delta. */
static inline void
rev_revision_set_delta_forw (struct rev_revision *obj, struct rev_delta *delta)
{
	assert (obj);
	if (obj->delta_forw) {
		rev_delta_dissociate (obj->delta_forw);
		obj->delta_forw = NULL;
	}
	if (delta) {
		rev_delta_associate (delta);
		obj->delta_forw = delta;
	}
}

/* This returns the reverse delta. */
static inline struct rev_delta *
rev_revision_get_delta_rev (struct rev_revision *obj)
{
	assert (obj);
	return obj->delta_rev;
}

/* This sets the reverse delta. */
static inline void
rev_revision_set_delta_rev (struct rev_revision *obj, struct rev_delta *delta)
{
	assert (obj);
	if (obj->delta_forw) {
		rev_delta_dissociate (obj->delta_rev);
		obj->delta_rev = NULL;
	}
	if (delta) {
		rev_delta_associate (delta);
		obj->delta_rev = delta;
	}
}

/* This returns the revision number. */
static inline int
rev_revision_get_number (struct rev_revision *obj)
{
	assert (obj);
	return obj->number;
}

/* This sets the revision number. */
static inline void
rev_revision_set_number (struct rev_revision *obj, int number)
{
	assert (obj);
	obj->number = number;
}

/* This returns the checkin time. */
static inline struct rev_datetime *
rev_revision_get_ctime (struct rev_revision *obj)
{
	assert (obj);
	return obj->ctime;
}

/* This sets the checkin time. */
static inline void
rev_revision_set_ctime (struct rev_revision *obj, struct rev_datetime *ctime)
{
	assert (obj);
	if (obj->ctime) rev_datetime_dissociate (obj->ctime);
	if (ctime) rev_datetime_associate (ctime);
	obj->ctime = ctime;
}

/* This returns the author's username string. */
static inline char *
rev_revision_get_author (struct rev_revision *obj)
{
	assert (obj);
	return obj->author;
}

/* This sets the author's username string. */
static inline void
rev_revision_set_author (struct rev_revision *obj, char *author)
{
	assert (obj);
	if (obj->author) free (obj->author);
	obj->author = author ? strdup (author) : NULL;
}

/* This returns the checkin comment string. */
static inline char *
rev_revision_get_comment (struct rev_revision *obj)
{
	assert (obj);
	return obj->comment;
}

/* This sets the checkin comment string. */
static inline void
rev_revision_set_comment (struct rev_revision *obj, char *comment)
{
	assert (obj);
	if (obj->comment) free (obj->comment);
	obj->comment = comment ? strdup (comment) : NULL;
}

/* This determines whether the revision is 'dummy' (see branch.h for
 * description of this). */
#define rev_revision_is_dummy(rev) (! rev->branch)

	
#ifdef __cplusplus
}
#endif

#endif
