/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
#if !defined(ORG_FOSSIL_SCM_FSL_VPATH_H_INCLUDED)
#define ORG_FOSSIL_SCM_FSL_VPATH_H_INCLUDED
/*
  Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt

  SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  SPDX-FileCopyrightText: 2021 The Libfossil Authors
  SPDX-ArtifactOfProjectName: Libfossil
  SPDX-FileType: Code

  Heavily indebted to the Fossil SCM project (https://fossil-scm.org).

  *****************************************************************************
  This file declares public APIs relating to calculating paths via
  Fossil SCM version history.
*/

#include "internal.h" /* MUST come first b/c of config macros */

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct fsl_vpath_node fsl_vpath_node;
typedef struct fsl_vpath fsl_vpath;

/**
   Holds information for a single node in a path of checkin versions.

   @see fsl_vpath
*/
struct fsl_vpath_node {
  /** ID for this node */
  fsl_id_t rid;
  /** True if pFrom is the parent of rid */
  bool fromIsParent;
  /** True if primary side of common ancestor */
  bool isPrimary;
  /* HISTORICAL: Abbreviate output in "fossil bisect ls" */
  bool isHidden;
  /** Node this one came from. */
  fsl_vpath_node *pFrom;
  union {
    /** List of nodes of the same generation */
    fsl_vpath_node *pPeer;
    /** Next on path from beginning to end */
    fsl_vpath_node *pTo;
  } u;
  /** List of all nodes */
  fsl_vpath_node *pAll;
};

/**
   A utility type for collecting "paths" between two checkin versions.
*/
struct fsl_vpath{
  /** Current generation of nodes */
  fsl_vpath_node *pCurrent;
  /** All nodes */
  fsl_vpath_node *pAll;
  /** Nodes seen before */
  fsl_id_bag seen;
  /** Number of steps from first to last. */
  int nStep;
  /** Earliest node in the path. */
  fsl_vpath_node *pStart;
  /** Common ancestor of pStart and pEnd */
  fsl_vpath_node *pPivot;
  /** Most recent node in the path. */
  fsl_vpath_node *pEnd;
};

/**
   An empty-initialize fsl_vpath object, intended for const-copy
   initialization.
*/
#define fsl_vpath_empty_m {0,0,fsl_id_bag_empty_m,0,0,0,0}

/**
   An empty-initialize fsl_vpath object, intended for copy
   initialization.
*/
FSL_EXPORT const fsl_vpath fsl_vpath_empty;


/**
   Returns the first node in p's path.

   The returned node is owned by path may be invalidated by any APIs
   which manipulate path.
*/
FSL_EXPORT fsl_vpath_node * fsl_vpath_first(fsl_vpath *p);

/**
   Returns the last node in p's path.

   The returned node is owned by path may be invalidated by any APIs
   which manipulate path.
*/
FSL_EXPORT fsl_vpath_node * fsl_vpath_last(fsl_vpath *p);

/**
   Returns the next node p's path.

   The returned node is owned by path may be invalidated by any APIs
   which manipulate path.

   Intended to be used like this:

   ```
   for( p = fsl_vpath_first(path) ;
   p ;
   p = fsl_vpath_next(p)){
   ...
   }
   ```
*/
FSL_EXPORT fsl_vpath_node * fsl_vpath_next(fsl_vpath_node *p);

/**
   Returns p's path length.
*/
FSL_EXPORT int fsl_vpath_length(fsl_vpath const * p);

/**
   Frees all nodes in path (which must not be NULL) and resets all
   state in path. Does not free path.
*/
FSL_EXPORT void fsl_vpath_clear(fsl_vpath *path);

/**
   Find the mid-point of the path.  If the path contains fewer than 2
   steps, returns 0. The returned node is owned by path and may be
   invalidated by any APIs which manipulate path.
*/
FSL_EXPORT fsl_vpath_node * fsl_vpath_midpoint(fsl_vpath * path);


/**
   Computes the shortest path from checkin versions iFrom to iTo
   (inclusive), storing the result state in path. If path has
   state before this is called, it is cleared by this call.

   iFrom and iTo must both be valid checkin version RIDs.

   If directOnly is true, then use only the "primary" links from
   parent to child.  In other words, ignore merges.

   On success, returns 0 and path->pStart will point to the
   beginning of the path (the iFrom node). If pStart is 0 then no
   path could be found but 0 is still returned.

   Elements of the path can be traversed like so:

   ```
   fsl_vpath path = fsl_vpath_empty;
   fsl_vpath_node * n = 0;
   int rc = fsl_vpath_shortest(f, &path, versionFrom, versionTo, 1, 0);
   if(rc) { ... error ... }
   for( n = fsl_vpath_first(&path); n; n = fsl_vpath_next(n) ){
   ...
   }
   fsl_vpath_clear(&path);
   ```

   On error, f's error state may be updated with a description of the
   problem.
*/
FSL_EXPORT int fsl_vpath_shortest( fsl_cx * const f, fsl_vpath * const path,
                                   fsl_id_t iFrom, fsl_id_t iTo,
                                   bool directOnly, bool oneWayOnly );

/**
   This variant of fsl_vpath_shortest() stores the shortest direct
   path from version iFrom to version iTo in the ANCESTOR temporary
   table using f's current repo db handle. That table gets created, if
   needed, else cleared by this call.

   The ANCESTOR temp table has the following interface:

   ```
   rid INT UNIQUE
   generation INTEGER PRIMARY KEY
   ```

   Where [rid] is a checkin version RID and [generation] is the
   1-based number of steps from iFrom, including iFrom (so iFrom's own
   generation is 1).

   On error returns 0 and, if pSteps is not NULL, assigns *pSteps to
   the number of entries added to the ancestor table. On error, pSteps
   is never modifed, any number of various non-0 codes may be
   returned, and f's error state will, if possible (not an OOM), be
   updated to describe the problem.

   This function's name is far too long and descriptive. We might want
   to consider something shorter.

   Maintenance reminder: this impl swaps the 2nd and 3rd parameters
   compared to fossil(1)'s version!
*/
FSL_EXPORT int fsl_vpath_shortest_store_in_ancestor(fsl_cx * const f,
                                                    fsl_id_t iFrom,
                                                    fsl_id_t iTo,
                                                    uint32_t *pSteps);

/**
   Computes a list of direct (non-merge) ancestors of the given
   checkin RID and stores it in the TEMP table [ancestor], which it
   creates if needed or clears if it currently exists.

   The [ancestor] schema is described in
   fsl_vpath_shortest_store_in_ancestor(). The [generation] value of
   the record corresponding to rid is 1, increasing by 1 for each
   generation back in the history.

   Returns 0 on success, FSL_RC_NOT_A_REPO if f has no repo db opened,
   and any number of lower-level result codes if something goes wrong.
*/
FSL_EXPORT int fsl_compute_direct_ancestors(fsl_cx * const f, fsl_id_t rid);

/**
   Reconstructs path from path->pStart to path->pEnd, reversing its
   order by fiddling with the u->pTo fields.

   Unfortunately does not reverse after the initial creation/reversal
   :/.
*/
FSL_EXPORT void fsl_vpath_reverse(fsl_vpath * path);


#if defined(__cplusplus)
} /*extern "C"*/
#endif
#endif
/* ORG_FOSSIL_SCM_FSL_VPATH_H_INCLUDED */
