/* tla-project-tree.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * Copyright (C) 2005 Canonical Limited
 *    Authors: Robert Collins <robert.collins@canonical.com>
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/exception.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/char/str.h"
#include "hackerlab/mem/mem.h"
#include "libfsutils/file-contents.h"
#include "libawk/trim.h"
#include "libarch/ancestry.h"
#include "libarch/namespace.h"
#include "libarch/patch-id.h"
#include "libarch/patch-logs.h"
#include "libarch/changelogs.h"
#include "libarch/inv-ids.h"
#include "libarch/invent.h"
#include "libarch/project-tree.h"
#include "libarch/tla-project-tree.h"

static const char arch_tree_format_str_hackerlab[] = "Hackerlab arch project directory, format version 1.";
typedef struct tla_project_tree_ 
{
    arch_project_tree_t parent;
    cached_changeset_inventory_t * inventory_cache;
} tla_project_tree_t;

/* methods */
static void tla_init (arch_project_tree_t * tree);
static enum arch_id_tagging_method tla_id_tagging_method (arch_project_tree_t * tree, enum arch_inventory_category * cat_var, int strict);
static void tla_changeset_inventory_traveral (arch_project_tree_t * tree, struct arch_inventory_options * options, inv_callback callback, void * closure);
static void tla_project_tree_mutated (arch_project_tree_t * tree);
/* will become static eventually */
extern inventory_entry_t * tla_path_id (arch_project_tree_t * tree, int * errn, t_uchar const * const path, struct stat * known_lstat);
static int tla_project_tree_destructor(void * data);

arch_project_tree_vtable tla_project_tree_vtable = 
{
    tla_init,
    tla_id_tagging_method,
    tla_path_id,
    tla_changeset_inventory_traveral,
    tla_project_tree_mutated, 
    arch_project_tree_vtable_end
};

/**
 * \brief prepare a directory to be a tla tree
 */
void
tla_project_tree_init_directory (t_uchar const * const tree_root)
{
  t_uchar * arch_dir = 0;
  t_uchar * arch_vsn_file = 0;
  t_uchar * id_tagging_method_file = 0;
  t_uchar * id_tagging_method_defaults = 0;
  int out_fd;

  arch_dir = file_name_in_vicinity (0, tree_root, "{arch}");
  arch_vsn_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");
  id_tagging_method_file = file_name_in_vicinity (0, arch_dir, "=tagging-method");

  safe_mkdir (arch_dir, 0777);
  out_fd = safe_open (arch_vsn_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
  safe_printfmt (out_fd, "%s\n", arch_tree_format_str_hackerlab);
  safe_close (out_fd);

  id_tagging_method_defaults = arch_default_id_tagging_method_contents (arch_unspecified_id_tagging);
  out_fd = safe_open (id_tagging_method_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
  safe_printfmt (out_fd, "%s", id_tagging_method_defaults);
  safe_close (out_fd);

  lim_free (0, arch_dir);
  lim_free (0, arch_vsn_file);
  lim_free (0, id_tagging_method_defaults);
  lim_free (0, id_tagging_method_file);
}


/**
 * \brief is dir the root of a tla(hackerlab) project tree? 
 * \return non zero on true.
 */
int 
tla_project_tree_dir_is_root (void * context, t_uchar const * dir, arch_project_tree_t **out)
{
  int result = 0;
  t_uchar * arch_dir = file_name_in_vicinity (0, dir, "{arch}");
  t_uchar * arch_version_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");

  if (!safe_access (arch_version_file, F_OK))
    {
      t_uchar * content = trim_surrounding_ws(file_contents (arch_version_file));
      if (!str_cmp (content, arch_tree_format_str_hackerlab))
        {
          result = -1;
          if (out)
            {
              *out = (arch_project_tree_t *)talloc (context, tla_project_tree_t);
              talloc_set_destructor (*out, tla_project_tree_destructor);
              (*out)->vtable = &tla_project_tree_vtable;
            }
        }
      else
          /* incompatible tree */
        {
          safe_printfmt (2, "tla_project_tree_dir_is_root: unrecognized project tree format: '%s'\n", content);
          exit (2);
        }
      lim_free (0, content);
    }

  lim_free (0, arch_dir);
  lim_free (0, arch_version_file);
  return result;
}

/**
 * \brief find the id tagging method for a tree
 */
enum arch_id_tagging_method
tla_id_tagging_method (arch_project_tree_t * tree, enum arch_inventory_category * cat_var, int strict)
{
  struct arch_inventory_options options;

  mem_set0 ((t_uchar *)&options, sizeof (options));
  arch_get_inventory_naming_conventions (&options, tree);
  if (cat_var)
    {
      *cat_var = options.untagged_source_category;
    }

  tree->untagged_is_source = (options.untagged_source_category == arch_inventory_source);
  tree->tag_method = options.method;
  arch_free_inventory_naming_conventions (&options);

  return tree->tag_method;
}

/**
 * \brief see arch_project_tree_init
 */
void
tla_init (arch_project_tree_t * tree)
{
  /* perhaps this should call id_tagging_method ? */
  tree->untagged_is_source = 0;
  tree->tag_method = arch_names_id_tagging; /* ancient default */
  tree->id_tagging_shortcut = NULL;
  tree->explicit_skips = NULL;
  arch_tree_id_tagging_method (tree, NULL, 0);
  ((tla_project_tree_t *) tree)->inventory_cache = NULL;
}

/**
 * \brief destroy the resources of a tla tree
 */
int
tla_project_tree_destructor (void * data)
{
  tla_project_tree_t * tree = talloc_get_type (data, tla_project_tree_t);
  arch_project_tree_t * arch_tree = (arch_project_tree_t *) tree;
  if (!tree)
    Throw (exception (EINVAL, "invalid tree in tla_project_tree_destructor"));
  free_assoc_table (arch_tree->id_tagging_shortcut);
  arch_tree->id_tagging_shortcut = NULL;
  free_assoc_table (arch_tree->explicit_skips);
  arch_tree->explicit_skips = NULL;
  tree->inventory_cache = NULL;
  arch_project_tree_finalise (arch_tree);
  return 0;
}

/**
 * \brief see arch_project_tree_changeset_inventory_traversal
 */
void
tla_changeset_inventory_traveral (arch_project_tree_t * tree, struct arch_inventory_options * options, inv_callback callback, void * closure)
{
  cached_changeset_inventory_traveral (options, tree, callback, closure, &((tla_project_tree_t *)tree)->inventory_cache);
  talloc_steal (tree, ((tla_project_tree_t *)tree)->inventory_cache);
}

/**
 * \brief see arch_project_tree_mutated
 */
void 
tla_project_tree_mutated (arch_project_tree_t * tree)
{
  talloc_free (((tla_project_tree_t *)tree)->inventory_cache);
  ((tla_project_tree_t *)tree)->inventory_cache = NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1