/**
 * @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_diff_tree.h
 * @brief Generic diff handler. Replacing the old svn_wc_diff_callbacks4_t
 * infrastructure
 */

#ifndef SVN_DIFF_TREE_H
#define SVN_DIFF_TREE_H

#include "svn_types.h"

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

/*
 *                   About the diff tree processor.
 *
 * Subversion uses two kinds of editors to describe changes. One to
 * describe changes on how to *exactly* transform one tree to another tree,
 * as efficiently as possible and one to describe the difference between trees
 * in order to review the changes, or to allow applying them on a third tree
 * which is similar to those other trees.
 *
 * The first case was originally handled by svn_delta_editor_t and might be
 * replaced by svn_editor_t in a future version. This diff processor handles
 * the other case and as such forms the layer below our diff and merge
 * handling.
 *
 * The major difference between this and the other editors is that this diff
 * always provides access to the full text and/or properties in the left and
 * right tree when applicable to allow processor implementers to decide how
 * to interpret changes.
 *
 * Originally this diff processor was not formalized explicitly, but
 * informally handled by the working copy diff callbacks. These callbacks just
 * provided the information to drive a unified diff and a textual merge. To go
 * one step further and allow full tree conflict detection we needed a better
 * defined diff handling. Instead of adding yet a few more functions and
 * arguments to the already overloaded diff callbacks the api was completely
 * redesigned with a few points in mind.
 *
 *   * It must be able to drive the old callbacks interface without users
 *     noticing the difference (100% compatible).
 *     (Implemented as svn_wc__wrap_diff_callbacks())
 *
 *   * It should provide the information that was missing in the old interface,
 *     but required to close existing issues.
 *
 *     E.g. - properties and children on deleted directories.
 *          - revision numbers and copyfrom information on directories.
 *
 * To cleanup the implementation and make it easier on diff processors to
 * handle the results I also added the following constraints.
 *
 *   * Diffs should be fully reversable: anything that is deleted should be
 *     available, just like something that is added.
 *     (Proven via svn_diff__tree_processor_reverse_create)
 *     ### Still in doubt if *_deleted() needs a copy_to argument, for the
 *     ### 99% -> 100%.
 *
 *   * Diff processors should have an easy way to communicate that they are
 *     not interrested in certain expensive to obtain results.
 *
 *   * Directories should have clear open and close events to allow adding them
 *     before their children, but still allowing property changes to have
 *     defined behavior.
 *
 *   * Files and directories should be handled as similar as possible as in
 *     many cases they are just nodes in a tree.
 *
 *   * It should be easy to create diff wrappers to apply certain transforms.
 *
 * During the creation an additional requirement of knowing about 'some
 * absent' nodes was added, to allow the merge to work on just this processor
 * api.
 *
 * The api describes a clean open-close walk through a tree, depending on the
 * driver multiple siblings can be described at the same time, but when a
 * directory is closed all descendants are done.
 *
 * Note that it is possible for nodes to be described as a delete followed by
 * an add at the same place within one parent. (Iff the diff is reversed you
 * can see an add followed by a delete!)
 *
 * The directory batons live between the open and close events of a directory
 * and are thereby guaranteed to outlive the batons of their descendants.
 */

/* Describes the source of a merge */
typedef struct svn_diff_source_t
{
  /* Always available */
  svn_revnum_t revision;

  /* Depending on the driver available for copyfrom */
  const char *repos_relpath;
} svn_diff_source_t;

/**
 * A callback vtable invoked by our diff-editors, as they receive diffs
 * from the server. 'svn diff' and 'svn merge' implement their own versions
 * of this vtable.
 *
 * All callbacks receive the processor and at least a parent baton. Forwarding
 * the processor allows future extensions to call into the old functions without
 * revving the entire API.
 *
 * Users must call svn_diff__tree_processor_create() to allow adding new
 * callbacks later. (E.g. when we decide how to add move support) These
 * extensions can then just call into other callbacks.
 *
 * @since New in 1.8.
 */
typedef struct svn_diff_tree_processor_t
{
  /** The value passed to svn_diff__tree_processor_create() as BATON.
   */
  void *baton; /* To avoid an additional in some places */

  /* Called before a directories children are processed.
   *
   * Set *SKIP_CHILDREN to TRUE, to skip calling callbacks for all
   * children.
   *
   * Set *SKIP to TRUE to skip calling the added, deleted, changed
   * or closed callback for this node only.
   */
  svn_error_t *
  (*dir_opened)(void **new_dir_baton,
                svn_boolean_t *skip,
                svn_boolean_t *skip_children,
                const char *relpath,
                const svn_diff_source_t *left_source,
                const svn_diff_source_t *right_source,
                const svn_diff_source_t *copyfrom_source,
                void *parent_dir_baton,
                const struct svn_diff_tree_processor_t *processor,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool);

  /* Called after a directory and all its children are added
   */
  svn_error_t *
  (*dir_added)(const char *relpath,
               const svn_diff_source_t *copyfrom_source,
               const svn_diff_source_t *right_source,
               /*const*/ apr_hash_t *copyfrom_props,
               /*const*/ apr_hash_t *right_props,
               void *dir_baton,
               const struct svn_diff_tree_processor_t *processor,
               apr_pool_t *scratch_pool);

  /* Called after all children of this node are reported as deleted.
   *
   * The default implementation calls dir_closed().
   */
  svn_error_t *
  (*dir_deleted)(const char *relpath,
                 const svn_diff_source_t *left_source,
                 /*const*/ apr_hash_t *left_props,
                 void *dir_baton,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *scratch_pool);

  /* Called instead of dir_closed() if the properties on the directory
   *  were modified.
   *
   * The default implementation calls dir_closed().
   */
  svn_error_t *
  (*dir_changed)(const char *relpath,
                 const svn_diff_source_t *left_source,
                 const svn_diff_source_t *right_source,
                 /*const*/ apr_hash_t *left_props,
                 /*const*/ apr_hash_t *right_props,
                 const apr_array_header_t *prop_changes,
                 void *dir_baton,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *scratch_pool);

  /* Called when a directory is closed without applying changes to
   * the directory itself.
   *
   * When dir_changed or dir_deleted are handled by the default implementation
   * they call dir_closed()
   */
  svn_error_t *
  (*dir_closed)(const char *relpath,
                const svn_diff_source_t *left_source,
                const svn_diff_source_t *right_source,
                void *dir_baton,
                const struct svn_diff_tree_processor_t *processor,
                apr_pool_t *scratch_pool);

  /* Called before file_added(), file_deleted(), file_changed() and
     file_closed()
   */
  svn_error_t *
  (*file_opened)(void **new_file_baton,
                 svn_boolean_t *skip,
                 const char *relpath,
                 const svn_diff_source_t *left_source,
                 const svn_diff_source_t *right_source,
                 const svn_diff_source_t *copyfrom_source,
                 void *dir_baton,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool);

  /* Called after file_opened() for newly added and copied files */
  svn_error_t *
  (*file_added)(const char *relpath,
                const svn_diff_source_t *copyfrom_source,
                const svn_diff_source_t *right_source,
                const char *copyfrom_file,
                const char *right_file,
                /*const*/ apr_hash_t *copyfrom_props,
                /*const*/ apr_hash_t *right_props,
                void *file_baton,
                const struct svn_diff_tree_processor_t *processor,
                apr_pool_t *scratch_pool);

  /* Called after file_opened() for deleted or moved away files */
  svn_error_t *
  (*file_deleted)(const char *relpath,
                  const svn_diff_source_t *left_source,
                  const char *left_file,
                  /*const*/ apr_hash_t *left_props,
                  void *file_baton,
                  const struct svn_diff_tree_processor_t *processor,
                  apr_pool_t *scratch_pool);

  /* Called after file_opened() for changed files */
  svn_error_t *
  (*file_changed)(const char *relpath,
                  const svn_diff_source_t *left_source,
                  const svn_diff_source_t *right_source,
                  const char *left_file,
                  const char *right_file,
                  /*const*/ apr_hash_t *left_props,
                  /*const*/ apr_hash_t *right_props,
                  svn_boolean_t file_modified,
                  const apr_array_header_t *prop_changes,
                  void *file_baton,
                  const struct svn_diff_tree_processor_t *processor,
                  apr_pool_t *scratch_pool);

  /* Called after file_opened() for unmodified files */
  svn_error_t *
  (*file_closed)(const char *relpath,
                 const svn_diff_source_t *left_source,
                 const svn_diff_source_t *right_source,
                 void *file_baton,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *scratch_pool);

  /* Called when encountering a marker for an absent file or directory */
  svn_error_t *
  (*node_absent)(const char *relpath,
                 void *dir_baton,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *scratch_pool);
} svn_diff_tree_processor_t;

/**
 * Create a new svn_diff_tree_processor_t instance with all functions
 * set to a callback doing nothing but copying the parent baton to
 * the new baton.
 *
 * @since New in 1.8.
 */
svn_diff_tree_processor_t *
svn_diff__tree_processor_create(void *baton,
                                apr_pool_t *result_pool);

/**
 * Create a new svn_diff_tree_processor_t instance with all functions setup
 * to call into another svn_diff_tree_processor_t processor, but with all
 * adds and deletes inverted.
 *
 * @since New in 1.8.
 */ /* Used by libsvn clients repository diff */
const svn_diff_tree_processor_t *
svn_diff__tree_processor_reverse_create(const svn_diff_tree_processor_t * processor,
                                        const char *prefix_relpath,
                                        apr_pool_t *result_pool);

/**
 * Create a new svn_diff_tree_processor_t instance with all functions setup
 * to call into processor for all paths equal to and below prefix_relpath.
 *
 * @since New in 1.8.
 */ /* Used by libsvn clients repository diff */
const svn_diff_tree_processor_t *
svn_diff__tree_processor_filter_create(const svn_diff_tree_processor_t *processor,
                                       const char *prefix_relpath,
                                       apr_pool_t *result_pool);

/**
 * Create a new svn_diff_tree_processor_t instace with all function setup
 * to call into processor with all adds with copyfrom information transformed
 * to simple node changes.
 *
 * @since New in 1.8.
 */ /* Used by libsvn_wc diff editor */
const svn_diff_tree_processor_t *
svn_diff__tree_processor_copy_as_changed_create(
                                const svn_diff_tree_processor_t *processor,
                                apr_pool_t *result_pool);


/**
 * Create a new svn_diff_tree_processor_t instance with all functions setup
 * to first call into processor1 and then processor2.
 *
 * This function is mostly a debug and migration helper.
 *
 * @since New in 1.8.
 */ /* Used by libsvn clients repository diff */
const svn_diff_tree_processor_t *
svn_diff__tree_processor_tee_create(const svn_diff_tree_processor_t *processor1,
                                    const svn_diff_tree_processor_t *processor2,
                                    apr_pool_t *result_pool);


svn_diff_source_t *
svn_diff__source_create(svn_revnum_t revision,
                        apr_pool_t *result_pool);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif  /* SVN_DIFF_TREE_H */

