/* 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 "libfsutils/file-contents.h"
#include "libarch/patch-logs.h"
#include "libarch/namespace.h"
#include "libarch/merge-points.h"

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



rel_table
arch_tree_merge_points (t_uchar * tree_root,
                        t_uchar * into_archive, t_uchar * into_spec,
                        t_uchar * from_archive, t_uchar * from_spec)
{
  rel_table answer = 0;
  rel_table source_logs = 0;
  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 (into_spec, path, 0), 0);

      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 (source_logs[x][1]);

      arch_add_merge_points (&answer, 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, t_uchar * into_spec,
                           t_uchar * from_archive, t_uchar * from_spec,
                           int upto)
{
  rel_table answer = 0;
  rel_table source_revisions = 0;
  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_make_record (into_spec, 0), 0);
      else
        {
          t_uchar * into_version = 0;
          rel_table all_source_revisions = 0;

          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 (all_source_revisions[x]), 0);
              if (!str_cmp (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->official_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, source_revisions[x][0]);
      level = arch_parse_package_name (arch_ret_patch_level, 0, 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 (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  rel_table merge_points = 0;
  rel_table all_logs = 0;
  rel_table answer = 0;

  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;
}

/* find all tree logs that are in tree and not in reference_tree 
 * TODO tests !
 */
rel_table
arch_new_in_tree (arch_project_tree_t *tree, t_uchar *reference_tree_root)
{
  rel_table all_logs = 0;
  rel_table reference_logs = 0;
  rel_table answer = 0;

  all_logs = arch_all_logs (tree->root);
  reference_logs = arch_all_logs (reference_tree_root);

  rel_sort_table_by_field (0, all_logs, 0);
  rel_sort_table_by_field (0, reference_logs, 0);

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

  rel_free_table (reference_logs);
  rel_free_table (all_logs);

  return answer;
}



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

  arch_parse_log (0, &headers, 0, log);
  new_patches_header = assoc_ref (headers, "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, new_patches[x][0]))
        rel_add_records (out, rel_make_record (level, new_patches[x][0], 0), 0);
    }

  free_assoc_table (headers);
  rel_free_table (new_patches);
}

static t_uchar *
arch_merge_points_prefix (t_uchar * from_archive, 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)
 */


syntax highlighted by Code2HTML, v. 0.9.1