/**
 * @copyright
 * ====================================================================
 *    Licensed to the Apache Software Foundation (ASF) under one
 *    or more contributor license agreements.  See the NOTICE file
 *    distributed with this work for additional information
 *    regarding copyright ownership.  The ASF licenses this file
 *    to you under the Apache License, Version 2.0 (the
 *    "License"); you may not use this file except in compliance
 *    with the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing,
 *    software distributed under the License is distributed on an
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *    KIND, either express or implied.  See the License for the
 *    specific language governing permissions and limitations
 *    under the License.
 * ====================================================================
 * @endcopyright
 *
 * @file svn_element.h
 * @brief Tree elements
 *
 * @since New in ???.
 */

#ifndef SVN_BRANCH_ELEMENT_H
#define SVN_BRANCH_ELEMENT_H

#include <apr_pools.h>
#include <apr_tables.h>

#include "svn_types.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* ====================================================================== */

/** Like apr_hash_get() but the hash key is an integer. */
void *
svn_eid__hash_get(apr_hash_t *ht,
                  int key);

/** Like apr_hash_set() but the hash key is an integer. */
void
svn_eid__hash_set(apr_hash_t *ht,
                  int key,
                  const void *val);

/** Like apr_hash_this_key() but the hash key is an integer. */
int
svn_eid__hash_this_key(apr_hash_index_t *hi);

struct svn_sort__item_t;

/** A hash iterator for iterating over an array or a hash table in
 * its natural order or in sorted order.
 *
 * For an array, the @a i and @a val members provide the index and value
 * of the current item.
 */
typedef struct svn_eid__hash_iter_t
{
  /* private: an array of (svn_sort__item_t) hash items for sorted iteration */
  const apr_array_header_t *array;

  /* current element: iteration order index */
  int i;
  /* current element: key */
  int eid;
  /* current element: value */
  void *val;
} svn_eid__hash_iter_t;

svn_eid__hash_iter_t *
svn_eid__hash_sorted_first(apr_pool_t *pool,
                           apr_hash_t *ht,
                           int (*comparison_func)(const struct svn_sort__item_t *,
                                                  const struct svn_sort__item_t *));

svn_eid__hash_iter_t *
svn_eid__hash_sorted_next(svn_eid__hash_iter_t *hi);

/** A sort ordering callback function that returns an indication whether
 * A sorts before or after or equal to B, by comparing their keys as EIDs.
 */
int
svn_eid__hash_sort_compare_items_by_eid(const struct svn_sort__item_t *a,
                                        const struct svn_sort__item_t *b);

#define SVN_EID__HASH_ITER_SORTED(i, ht, comparison_func, pool) \
  i = (void *)svn_eid__hash_sorted_first(pool, ht, comparison_func); \
  i; \
  i = (void *)svn_eid__hash_sorted_next((void *)i)

#define SVN_EID__HASH_ITER_SORTED_BY_EID(i, ht, pool) \
  SVN_EID__HASH_ITER_SORTED(i, ht, svn_eid__hash_sort_compare_items_by_eid, pool)


/* ====================================================================== */

/**
 */
typedef struct svn_element__branch_ref_t
{
  svn_revnum_t rev;
  const char *branch_id;
  int eid;
} svn_element__branch_ref_t;

/** Versioned payload of an element, excluding tree structure information.
 *
 * This specifies the properties and the text of a file or target of a
 * symlink, directly, or by reference to an existing committed element, or
 * by a delta against such a reference payload.
 *
 * ### An idea: If the sender and receiver agree, the payload for an element
 *     may be specified as "null" to designate that the payload is not
 *     available. For example, when a client performing a WC update has
 *     no read authorization for a given path, the server may send null
 *     payload and the client may record an 'absent' WC node. (This
 *     would not make sense in a commit.)
 */
typedef struct svn_element__payload_t svn_element__payload_t;

/*
 * ========================================================================
 * Element Payload Interface
 * ========================================================================
 *
 * @defgroup svn_element_payload Element payload interface
 * @{
 */

/** Versioned payload of a node, excluding tree structure information.
 *
 * Payload is described by setting fields in one of the following ways.
 * Other fields SHOULD be null (or equivalent).
 *
 *   by reference:  (kind=unknown, ref)
 *   dir:           (kind=dir, props)
 *   file:          (kind=file, props, text)
 *   symlink:       (kind=symlink, props, target)
 *
 * ### Idea for the future: Specify payload as an (optional) reference
 *     plus (optional) overrides or deltas against the reference?
 */
struct svn_element__payload_t
{
  /* Is this a subbranch-root element, in other words a link to a nested
   * branch? If so, all other fields are irrelevant. */
  svn_boolean_t is_subbranch_root;

  /* The node kind for this payload: dir, file, symlink, or unknown. */
  svn_node_kind_t kind;

  /* Reference an existing, committed payload. (Use with kind=unknown if
   * there is no content in props/text/targe fields.)
   * The 'null' value is (SVN_INVALID_REVNUM, NULL, *). */
  svn_element__branch_ref_t branch_ref;

  /* The pool in which the payload's content is allocated. Used when
   * resolving (populating the props/text/target in) a payload that was
   * originally defined by reference. */
  apr_pool_t *pool;

  /* Properties (for kind != unknown).
   * Maps (const char *) name -> (svn_string_t) value.
   * An empty hash means no properties. (SHOULD NOT be NULL.)
   * ### Presently NULL means 'no change' in some contexts. */
  apr_hash_t *props;

  /* File text (for kind=file; otherwise SHOULD be NULL). */
  svn_stringbuf_t *text;

  /* Symlink target (for kind=symlink; otherwise SHOULD be NULL). */
  const char *target;

};

/* Return true iff PAYLOAD satisfies all its invariants.
 */
svn_boolean_t
svn_element__payload_invariants(const svn_element__payload_t *payload);

/** Duplicate a node-payload @a old into @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_dup(const svn_element__payload_t *old,
                         apr_pool_t *result_pool);

/* Return true iff the payload of LEFT is identical to that of RIGHT.
 * References are not supported. Node kind 'unknown' is not supported.
 */
svn_boolean_t
svn_element__payload_equal(const svn_element__payload_t *left,
                           const svn_element__payload_t *right,
                           apr_pool_t *scratch_pool);

/** Create a new node-payload object for a subbranch-root (link to a
 * nested branch).
 *
 * Allocate the result in @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_create_subbranch(apr_pool_t *result_pool);

/** Create a new node-payload object by reference to an existing payload.
 *
 * Set the node kind to 'unknown'.
 *
 * Allocate the result in @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_create_ref(svn_revnum_t rev,
                                const char *branch_id,
                                int eid,
                                apr_pool_t *result_pool);

/** Create a new node-payload object for a directory node.
 *
 * Allocate the result in @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_create_dir(apr_hash_t *props,
                                apr_pool_t *result_pool);

/** Create a new node-payload object for a file node.
 *
 * Allocate the result in @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_create_file(apr_hash_t *props,
                                 svn_stringbuf_t *text,
                                 apr_pool_t *result_pool);

/** Create a new node-payload object for a symlink node.
 *
 * Allocate the result in @a result_pool.
 */
svn_element__payload_t *
svn_element__payload_create_symlink(apr_hash_t *props,
                                    const char *target,
                                    apr_pool_t *result_pool);

/** @} */


/*
 * ========================================================================
 * Element-Revision Content
 * ========================================================================
 *
 * @defgroup svn_el_rev_content Element-Revision Content
 * @{
 */

/* The content (parent, name and payload) of an element-revision.
 * In other words, an el-rev node in a (mixed-rev) directory-tree.
 */
typedef struct svn_element__content_t
{
  /* eid of the parent element, or -1 if this is the root element */
  int parent_eid;
  /* element name, or "" for root element; never null */
  const char *name;
  /* payload (kind, props, text, ...) */
  svn_element__payload_t *payload;

} svn_element__content_t;

/* Return a new content object constructed with deep copies of PARENT_EID,
 * NAME and PAYLOAD, allocated in RESULT_POOL.
 */
svn_element__content_t *
svn_element__content_create(int parent_eid,
                            const char *name,
                            const svn_element__payload_t *payload,
                            apr_pool_t *result_pool);

/* Return a deep copy of OLD, allocated in RESULT_POOL.
 */
svn_element__content_t *
svn_element__content_dup(const svn_element__content_t *old,
                         apr_pool_t *result_pool);

/* Return TRUE iff CONTENT_LEFT is the same as CONTENT_RIGHT. */
svn_boolean_t
svn_element__content_equal(const svn_element__content_t *content_left,
                           const svn_element__content_t *content_right,
                           apr_pool_t *scratch_pool);

/** @} */


/*
 * ========================================================================
 * Element Tree
 * ========================================================================
 *
 * The elements in an Element Tree do not necessarily form a single,
 * complete tree at all times.
 *
 * @defgroup svn_element_tree Element Tree
 * @{
 */

/* A (sub)tree of elements.
 *
 * An element tree is described by the content of element ROOT_EID in E_MAP,
 * and its children (as determined by their parent links) and their names
 * and their content recursively. For the element ROOT_EID itself, only
 * its content is relevant; its parent and name are to be ignored.
 *
 * E_MAP may also contain entries that are not part of the subtree. Thus,
 * to select a sub-subtree, it is only necessary to change ROOT_EID.
 *
 * The EIDs used in here may be considered either as global EIDs (known to
 * the repo), or as local stand-alone EIDs (in their own local name-space),
 * according to the context.
 */
typedef struct svn_element__tree_t
{
  /* EID -> svn_element__content_t mapping. */
  apr_hash_t *e_map;

  /* Subtree root EID. (ROOT_EID must be an existing key in E_MAP.) */
  int root_eid;

} svn_element__tree_t;

/* Create an element tree object.
 *
 * The result contains a *shallow* copy of E_MAP, or a new empty mapping
 * if E_MAP is null.
 */
svn_element__tree_t *
svn_element__tree_create(apr_hash_t *e_map,
                         int root_eid,
                         apr_pool_t *result_pool);

svn_element__content_t *
svn_element__tree_get(const svn_element__tree_t *tree,
                      int eid);

void
svn_element__tree_set(svn_element__tree_t *tree,
                      int eid,
                      const svn_element__content_t *element);

/* Purge entries from E_MAP that don't connect, via parent directory hierarchy,
 * to ROOT_EID. In other words, remove elements that have been implicitly
 * deleted.
 *
 * ROOT_EID must be present in E_MAP.
 *
 * ### Does not detect cycles: current implementation will not purge a cycle
 *     that is disconnected from ROOT_EID. This could be a problem.
 */
void
svn_element__tree_purge_orphans(apr_hash_t *e_map,
                                int root_eid,
                                apr_pool_t *scratch_pool);

/* Return the subtree-relative path of element EID in TREE.
 *
 * If the element EID does not currently exist in TREE, return NULL.
 *
 * ### TODO: Clarify sequencing requirements.
 */
const char *
svn_element__tree_get_path_by_eid(const svn_element__tree_t *tree,
                                  int eid,
                                  apr_pool_t *result_pool);

/* Return the subtree rooted at EID within ELEMENT_TREE.
 *
 * The result is limited by the lifetime of ELEMENT_TREE. It includes a
 * shallow copy of the mapping in ELEMENT_TREE: the hash table is
 * duplicated but the keys and values (element content data) are not.
 */
svn_element__tree_t *
svn_element__tree_get_subtree_at_eid(svn_element__tree_t *element_tree,
                                     int eid,
                                     apr_pool_t *result_pool);

/** @} */


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* SVN_BRANCH_ELEMENT_H */
