#ifndef ODB_SOURCE_H
#define ODB_SOURCE_H

#include "object.h"
#include "odb.h"

enum odb_source_type {
	/*
	 * The "unknown" type, which should never be in use. This type mostly
	 * exists to catch cases where the type field remains zeroed out.
	 */
	ODB_SOURCE_UNKNOWN,

	/* The "files" backend that uses loose objects and packfiles. */
	ODB_SOURCE_FILES,

	/* The "loose" backend that uses loose objects, only. */
	ODB_SOURCE_LOOSE,

	/* The "in-memory" backend that stores objects in memory. */
	ODB_SOURCE_INMEMORY,
};

struct object_id;
struct odb_read_stream;
struct strvec;

/*
 * The source is the part of the object database that stores the actual
 * objects. It thus encapsulates the logic to read and write the specific
 * on-disk format. An object database can have multiple sources:
 *
 *   - The primary source, which is typically located in "$GIT_DIR/objects".
 *     This is where new objects are usually written to.
 *
 *   - Alternate sources, which are configured via "objects/info/alternates" or
 *     via the GIT_ALTERNATE_OBJECT_DIRECTORIES environment variable. These
 *     alternate sources are only used to read objects.
 */
struct odb_source {
	struct odb_source *next;

	/* Object database that owns this object source. */
	struct object_database *odb;

	/* The type used by this source. */
	enum odb_source_type type;

	/*
	 * Figure out whether this is the local source of the owning
	 * repository, which would typically be its ".git/objects" directory.
	 * This local object directory is usually where objects would be
	 * written to.
	 */
	bool local;

	/*
	 * This object store is ephemeral, so there is no need to fsync.
	 */
	int will_destroy;

	/*
	 * Path to the source. If this is a relative path, it is relative to
	 * the current working directory.
	 */
	char *path;

	/*
	 * This callback is expected to free the underlying object database source and
	 * all associated resources. The function will never be called with a NULL pointer.
	 */
	void (*free)(struct odb_source *source);

	/*
	 * This callback is expected to close any open resources, like for
	 * example file descriptors or connections. The source is expected to
	 * still be usable after it has been closed. Closed resources may need
	 * to be reopened in that case.
	 */
	void (*close)(struct odb_source *source);

	/*
	 * This callback is expected to clear underlying caches of the object
	 * database source. The function is called when the repository has for
	 * example just been repacked so that new objects will become visible.
	 */
	void (*reprepare)(struct odb_source *source);

	/*
	 * This callback is expected to read object information from the object
	 * database source. The object info will be partially populated with
	 * pointers for each bit of information that was requested by the
	 * caller.
	 *
	 * The flags field is a combination of `OBJECT_INFO` flags. Only the
	 * following fields need to be handled by the backend:
	 *
	 *   - `OBJECT_INFO_QUICK` indicates it is fine to use caches without
	 *     re-verifying the data.
	 *
	 *   - `OBJECT_INFO_SECOND_READ` indicates that the initial object
	 *     lookup has failed and that the object sources should check
	 *     whether any of its on-disk state has changed that may have
	 *     caused the object to appear. Sources are free to ignore the
	 *     second read in case they know that the first read would have
	 *     already surfaced the object without reloading any on-disk state.
	 *
	 * The callback is expected to return a negative error code in case
	 * reading the object has failed, 0 otherwise.
	 */
	int (*read_object_info)(struct odb_source *source,
				const struct object_id *oid,
				struct object_info *oi,
				enum object_info_flags flags);

	/*
	 * This callback is expected to create a new read stream that can be
	 * used to stream the object identified by the given ID.
	 *
	 * The callback is expected to return a negative error code in case
	 * creating the object stream has failed, 0 otherwise.
	 */
	int (*read_object_stream)(struct odb_read_stream **out,
				  struct odb_source *source,
				  const struct object_id *oid);

	/*
	 * This callback is expected to iterate over all objects stored in this
	 * source and invoke the callback function for each of them. It is
	 * valid to yield the same object multiple time. A non-zero exit code
	 * from the object callback shall abort iteration.
	 *
	 * The optional `request` structure should serve as a template for
	 * looking up object info for every individual iterated object. It
	 * should not be modified directly and should instead be copied into a
	 * separate `struct object_info` that gets passed to the callback. If
	 * the caller passes a `NULL` pointer then the object itself shall not
	 * be read.
	 *
	 * The callback is expected to return a negative error code in case the
	 * iteration has failed to read all objects, 0 otherwise. When the
	 * callback function returns a non-zero error code then that error code
	 * should be returned.
	 */
	int (*for_each_object)(struct odb_source *source,
			       const struct object_info *request,
			       odb_for_each_object_cb cb,
			       void *cb_data,
			       const struct odb_for_each_object_options *opts);

	/*
	 * This callback is expected to count objects in the given object
	 * database source. The callback function does not have to guarantee
	 * that only unique objects are counted. The result shall be assigned
	 * to the `out` pointer.
	 *
	 * Accepts `enum odb_count_objects_flag` flags to alter the behaviour.
	 *
	 * The callback is expected to return 0 on success, or a negative error
	 * code otherwise.
	 */
	int (*count_objects)(struct odb_source *source,
			     enum odb_count_objects_flags flags,
			     unsigned long *out);

	/*
	 * This callback is expected to find the minimum required length to
	 * make the given object ID unique.
	 *
	 * The callback is expected to return a negative error code in case it
	 * failed, 0 otherwise.
	 */
	int (*find_abbrev_len)(struct odb_source *source,
			       const struct object_id *oid,
			       unsigned min_length,
			       unsigned *out);

	/*
	 * This callback is expected to freshen the given object so that its
	 * last access time is set to the current time. This is used to ensure
	 * that objects that are recent will not get garbage collected even if
	 * they were unreachable.
	 *
	 * Returns 0 in case the object does not exist, 1 in case the object
	 * has been freshened.
	 */
	int (*freshen_object)(struct odb_source *source,
			      const struct object_id *oid);

	/*
	 * This callback is expected to persist the given object into the
	 * object source. In case the object already exists it shall be
	 * freshened.
	 *
	 * The flags field is a combination of `WRITE_OBJECT` flags.
	 *
	 * The resulting object ID (and optionally the compatibility object ID)
	 * shall be written into the out pointers. The callback is expected to
	 * return 0 on success, a negative error code otherwise.
	 */
	int (*write_object)(struct odb_source *source,
			    const void *buf, unsigned long len,
			    enum object_type type,
			    struct object_id *oid,
			    struct object_id *compat_oid,
			    enum odb_write_object_flags flags);

	/*
	 * This callback is expected to persist the given object stream into
	 * the object source.
	 *
	 * The resulting object ID shall be written into the out pointer. The
	 * callback is expected to return 0 on success, a negative error code
	 * otherwise.
	 */
	int (*write_object_stream)(struct odb_source *source,
				   struct odb_write_stream *stream, size_t len,
				   struct object_id *oid);

	/*
	 * This callback is expected to create a new transaction that can be
	 * used to write objects to. The objects shall only be persisted into
	 * the object database when the transcation's commit function is
	 * called. Otherwise, the objects shall be discarded.
	 *
	 * Returns 0 on success, in which case the `*out` pointer will have
	 * been populated with the object database transaction. Returns a
	 * negative error code otherwise.
	 */
	int (*begin_transaction)(struct odb_source *source,
				 struct odb_transaction **out);

	/*
	 * This callback is expected to read the list of alternate object
	 * database sources connected to it and write them into the `strvec`.
	 *
	 * The result is expected to be paths to the alternates. All paths must
	 * be resolved to absolute paths.
	 *
	 * The callback is expected to return 0 on success, a negative error
	 * code otherwise.
	 */
	int (*read_alternates)(struct odb_source *source,
			       struct strvec *out);

	/*
	 * This callback is expected to persist the singular alternate passed
	 * to it into its list of alternates. Any pre-existing alternates are
	 * expected to remain active. Subsequent calls to `read_alternates` are
	 * thus expected to yield the pre-existing list of alternates plus the
	 * newly added alternate appended to its end.
	 *
	 * The callback is expected to return 0 on success, a negative error
	 * code otherwise.
	 */
	int (*write_alternate)(struct odb_source *source,
			       const char *alternate);
};

/*
 * Allocate and initialize a new source for the given object database located
 * at `path`. `local` indicates whether or not the source is the local and thus
 * primary object source of the object database.
 */
struct odb_source *odb_source_new(struct object_database *odb,
				  const char *path,
				  bool local);

/*
 * Initialize the source for the given object database located at `path`.
 * `local` indicates whether or not the source is the local and thus primary
 * object source of the object database.
 *
 * This function is only supposed to be called by specific object source
 * implementations.
 */
void odb_source_init(struct odb_source *source,
		     struct object_database *odb,
		     enum odb_source_type type,
		     const char *path,
		     bool local);

/*
 * Free the object database source, releasing all associated resources and
 * freeing the structure itself.
 */
void odb_source_free(struct odb_source *source);

/*
 * Release the object database source, releasing all associated resources.
 *
 * This function is only supposed to be called by specific object source
 * implementations.
 */
void odb_source_release(struct odb_source *source);

/*
 * Close the object database source without releasing he underlying data. The
 * source can still be used going forward, but it first needs to be reopened.
 * This can be useful to reduce resource usage.
 */
static inline void odb_source_close(struct odb_source *source)
{
	source->close(source);
}

/*
 * Reprepare the object database source and clear any caches. Depending on the
 * backend used this may have the effect that concurrently-written objects
 * become visible.
 */
static inline void odb_source_reprepare(struct odb_source *source)
{
	source->reprepare(source);
}

/*
 * Read an object from the object database source identified by its object ID.
 * Returns 0 on success, a negative error code otherwise.
 */
static inline int odb_source_read_object_info(struct odb_source *source,
					      const struct object_id *oid,
					      struct object_info *oi,
					      enum object_info_flags flags)
{
	return source->read_object_info(source, oid, oi, flags);
}

/*
 * Create a new read stream for the given object ID. Returns 0 on success, a
 * negative error code otherwise.
 */
static inline int odb_source_read_object_stream(struct odb_read_stream **out,
						struct odb_source *source,
						const struct object_id *oid)
{
	return source->read_object_stream(out, source, oid);
}

/*
 * Iterate through all objects contained in the given source and invoke the
 * callback function for each of them. Returning a non-zero code from the
 * callback function aborts iteration. There is no guarantee that objects
 * are only iterated over once.
 *
 * The optional `request` structure serves as a template for retrieving the
 * object info for each individual iterated object and will be populated as if
 * `odb_source_read_object_info()` was called on the object. It will not be
 * modified, the callback will instead be invoked with a separate `struct
 * object_info` for every object. Object info will not be read when passing a
 * `NULL` pointer.
 *
 * The flags is a bitfield of `ODB_FOR_EACH_OBJECT_*` flags. Not all flags may
 * apply to a specific backend, so whether or not they are honored is defined
 * by the implementation.
 *
 * Returns 0 when all objects have been iterated over, a negative error code in
 * case iteration has failed, or a non-zero value returned from the callback.
 */
static inline int odb_source_for_each_object(struct odb_source *source,
					     const struct object_info *request,
					     odb_for_each_object_cb cb,
					     void *cb_data,
					     const struct odb_for_each_object_options *opts)
{
	return source->for_each_object(source, request, cb, cb_data, opts);
}

/*
 * Count the number of objects in the given object database source.
 *
 * Returns 0 on success, a negative error code otherwise.
 */
static inline int odb_source_count_objects(struct odb_source *source,
					   enum odb_count_objects_flags flags,
					   unsigned long *out)
{
	return source->count_objects(source, flags, out);
}

/*
 * Determine the minimum required length to make the given object ID unique in
 * the given source. Returns 0 on success, a negative error code otherwise.
 */
static inline int odb_source_find_abbrev_len(struct odb_source *source,
					     const struct object_id *oid,
					     unsigned min_len,
					     unsigned *out)
{
	return source->find_abbrev_len(source, oid, min_len, out);
}

/*
 * Freshen an object in the object database by updating its timestamp.
 * Returns 1 in case the object has been freshened, 0 in case the object does
 * not exist.
 */
static inline int odb_source_freshen_object(struct odb_source *source,
					    const struct object_id *oid)
{
	return source->freshen_object(source, oid);
}

/*
 * Write an object into the object database source. Returns 0 on success, a
 * negative error code otherwise. Populates the given out pointers for the
 * object ID and the compatibility object ID, if non-NULL.
 */
static inline int odb_source_write_object(struct odb_source *source,
					  const void *buf, unsigned long len,
					  enum object_type type,
					  struct object_id *oid,
					  struct object_id *compat_oid,
					  enum odb_write_object_flags flags)
{
	return source->write_object(source, buf, len, type, oid,
				    compat_oid, flags);
}

/*
 * Write an object into the object database source via a stream. The overall
 * length of the object must be known in advance.
 *
 * Return 0 on success, a negative error code otherwise. Populates the given
 * out pointer for the object ID.
 */
static inline int odb_source_write_object_stream(struct odb_source *source,
						 struct odb_write_stream *stream,
						 size_t len,
						 struct object_id *oid)
{
	return source->write_object_stream(source, stream, len, oid);
}

/*
 * Read the list of alternative object database sources from the given backend
 * and populate the `strvec` with them. The listing is not recursive -- that
 * is, if any of the yielded alternate sources has alternates itself, those
 * will not be yielded as part of this function call.
 *
 * Return 0 on success, a negative error code otherwise.
 */
static inline int odb_source_read_alternates(struct odb_source *source,
					     struct strvec *out)
{
	return source->read_alternates(source, out);
}

/*
 * Write and persist a new alternate object database source for the given
 * source. Any preexisting alternates are expected to stay valid, and the new
 * alternate shall be appended to the end of the list.
 *
 * Returns 0 on success, a negative error code otherwise.
 */
static inline int odb_source_write_alternate(struct odb_source *source,
					      const char *alternate)
{
	return source->write_alternate(source, alternate);
}

/*
 * Create a new transaction that can be used to write objects into a temporary
 * staging area. The objects will only be persisted when the transaction is
 * committed.
 *
 * Returns 0 on success, a negative error code otherwise.
 */
static inline int odb_source_begin_transaction(struct odb_source *source,
					       struct odb_transaction **out)
{
	return source->begin_transaction(source, out);
}

#endif
