/* merge-points.c:
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/safe.h"
#include "tla/libfsutils/file-contents.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/merge-points.h"


/* __STDC__ prototypes for static functions */
static void arch_add_merge_points (rel_table * out,
                                   const t_uchar * level,
                                   const t_uchar * log,
                                   const t_uchar * prefix);
static t_uchar * arch_merge_points_prefix (const t_uchar * from_archive,
                                           const t_uchar * from_spec);



rel_table
arch_tree_merge_points (const t_uchar * tree_root,
                        const t_uchar * into_archive,
                        const t_uchar * into_spec,
                        const t_uchar * from_archive,
                        const t_uchar * from_spec)
{
  rel_table answer = rel_table_nil;
  rel_table source_logs = rel_table_nil;
  int x;
  t_uchar * prefix = 0;

  if (arch_valid_package_name (into_spec, arch_no_archive, arch_req_patch_level, 0))
    {
      t_uchar * path = 0;
      t_uchar * level = 0;

      path = arch_log_file (tree_root, into_archive, into_spec);
      level = arch_parse_package_name (arch_ret_patch_level, 0, into_spec);

      rel_add_records (&source_logs, rel_make_record_2_taking (rel_make_field_str (into_spec), rel_make_field_str (path)), rel_record_null);

      lim_free (0, path);
      lim_free (0, level);
    }
  else if (arch_valid_package_name (into_spec, arch_no_archive, arch_req_version, 0))
    {
      source_logs = arch_logs (tree_root, into_archive, into_spec, 0);
    }
  else
    panic ("invalid into_spec argument to arch_tree_merge_points");

  prefix = arch_merge_points_prefix (from_archive, from_spec);

  for (x = 0; x < rel_n_records (source_logs); ++x)
    {
      t_uchar * log_text = 0;

      log_text = file_contents (rel_peek_str (source_logs, x, 1));

      arch_add_merge_points (&answer, rel_peek_str (source_logs, x, 0), log_text, prefix);

      lim_free (0, log_text);
    }


  rel_free_table (source_logs);
  lim_free (0, prefix);

  return answer;
}


rel_table
arch_archive_merge_points (struct arch_archive * into_arch,
                           const t_uchar * into_spec,
                           const t_uchar * from_archive,
                           const t_uchar * from_spec,
                           int upto)
{
  rel_table answer = rel_table_nil;
  rel_table source_revisions = rel_table_nil;
  int x;
  t_uchar * prefix = 0;

  if (arch_valid_package_name (into_spec, arch_no_archive, arch_req_patch_level, 0))
    {
      if (!upto)
        rel_add_records (&source_revisions, rel_singleton_record_taking (rel_make_field_str (into_spec)), rel_record_null);
      else
        {
          t_uchar * into_version = 0;
          rel_table all_source_revisions = rel_table_nil;

          into_version = arch_parse_package_name (arch_ret_package_version, 0, into_spec);
          all_source_revisions = arch_archive_revisions (into_arch, into_version, 2);
          for (x = 0; x < rel_n_records (all_source_revisions); ++x)
            {
              rel_add_records (&source_revisions, rel_copy_record (rel_peek_record (all_source_revisions, x)), rel_record_null);
              if (!str_cmp (rel_peek_str (all_source_revisions, x, 0), into_spec))
                break;
            }

          if (x == rel_n_records (all_source_revisions))
            {
              safe_printfmt (2, "arch: attempt to compute merge-points of non-existent revision\n  (%s\%s)\n", into_arch->name, into_spec);
              exit (2);
            }

          lim_free (0, into_version);
          rel_free_table (all_source_revisions);
        }
    }
  else if (arch_valid_package_name (into_spec, arch_no_archive, arch_req_version, 0))
    {
      source_revisions = arch_archive_revisions (into_arch, into_spec, 2);
    }
  else
    panic ("invalid into_spec argument to arch_archive_merge_points");


  prefix = arch_merge_points_prefix (from_archive, from_spec);

  for (x = 0; x < rel_n_records (source_revisions); ++x)
    {
      t_uchar * log_text = 0;
      t_uchar * level = 0;

      log_text = arch_archive_log (into_arch, rel_peek_str (source_revisions, x, 0));
      level = arch_parse_package_name (arch_ret_patch_level, 0, rel_peek_str (source_revisions, x, 0));

      arch_add_merge_points (&answer, level, log_text, prefix);

      lim_free (0, log_text);
      lim_free (0, level);
    }


  rel_free_table (source_revisions);
  lim_free (0, prefix);

  return answer;
}


rel_table
arch_new_in_version (const t_uchar * tree_root,
                     const t_uchar * archive,
                     const t_uchar * version)
{
  rel_table merge_points = rel_table_nil;
  rel_table all_logs = rel_table_nil;
  rel_table answer = rel_table_nil;

  merge_points = arch_tree_merge_points (tree_root, archive, version, 0, 0);
  all_logs = arch_all_logs (tree_root);

  rel_sort_table_by_field (0, merge_points, 1);
  rel_sort_table_by_field (0, all_logs, 0);

  answer = rel_join (2, rel_join_output (2,0, -1), 1, 0, merge_points, all_logs);
  arch_sort_table_by_name_field (0, answer, 0);

  rel_free_table (merge_points);
  rel_free_table (all_logs);

  return answer;
}




static void
arch_add_merge_points (rel_table * out,
                       const t_uchar * level,
                       const t_uchar * log,
                       const t_uchar * prefix)
{
  assoc_table headers = 0;
  const t_uchar * new_patches_header;
  rel_table new_patches = rel_table_nil;
  int x;

  arch_parse_log (0, &headers, 0, log);
  new_patches_header = assoc_get_str_taking (headers, rel_make_field_str ("new-patches"));

  new_patches = rel_ws_split (new_patches_header);
  arch_sort_table_by_name_field (0, new_patches, 0);
  rel_uniq_by_field (&new_patches, 0);

  for (x = 0; x < rel_n_records (new_patches); ++x)
    {
      if (!str_cmp_prefix (prefix, rel_peek_str (new_patches, x, 0)))
        rel_add_records (out, rel_make_record_2_taking (rel_make_field_str (level), rel_get_field (new_patches, x, 0)), rel_record_null);
    }

  free_assoc_table (headers);
  rel_free_table (new_patches);
}


static t_uchar *
arch_merge_points_prefix (const t_uchar * from_archive,
                          const t_uchar * from_spec)
{
  if (!from_archive)
    return 0;
  else if (arch_valid_package_name (from_spec, arch_no_archive, arch_req_patch_level, 0))
    return arch_fully_qualify (from_archive, from_spec);
  else
    return str_alloc_cat_many (0, from_archive, "/", from_spec, "--", str_end);
}






/* tag: Tom Lord Tue May 27 18:04:17 2003 (merge-points.c)
 */
