/* tag.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/os/time.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "tla/libdate/date-string.h"
#include "tla/libfsutils/string-files.h"
#include "tla/libfsutils/copy-file.h"
#include "tla/libfsutils/tmp-files.h"
#include "tla/libfsutils/rmrf.h"
#include "tla/libarch/diffs.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/invent.h"
#include "tla/libarch/my.h"
#include "tla/libarch/hooks.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/pristines.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/make-changeset.h"
#include "tla/libarch/changeset-report.h"
#include "tla/libarch/local-cache.h"
#include "tla/libarch/inv-ids.h"
#include "tla/libarch/changelogs.h"
#include "tla/libarch/apply-changeset.h"
#include "tla/libarch/merge-points.h"
#include "tla/libarch/tag.h"


/* __STDC__ prototypes for static functions */
static t_uchar * arch_prepare_for_tag (t_uchar ** cooked_log_ret, struct arch_archive * arch, t_uchar * revision,
                                       struct arch_archive * from_arch, t_uchar * from_revision,
                                       t_uchar * raw_log);



void
arch_tag (int chatter_fd,
          struct arch_archive * arch,
          t_uchar * revision,
          struct arch_archive * from_arch,
          t_uchar * from_revision,
          t_uchar * raw_log)
{
  t_uchar * errstr;
  t_uchar * version = 0;
  t_uchar * this_level = 0;
  t_uchar * prev_revision = 0;
  t_uchar * prev_level = 0;
  t_uchar * continuation = 0;
  t_uchar * cooked_log = 0;
  t_uchar * my_uid = 0;
  t_uchar * txn_id = 0;
  t_uchar * tag_changeset = 0;

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  this_level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
  prev_revision = arch_previous_revision (arch, revision);
  if (prev_revision)
    prev_level = arch_parse_package_name (arch_ret_patch_level, 0, prev_revision);

  continuation = arch_fully_qualify (from_arch->name, from_revision);

  my_uid = arch_my_id_uid ();
  txn_id = arch_generate_txn_id ();

  tag_changeset = arch_prepare_for_tag (&cooked_log, arch, revision, from_arch, from_revision, raw_log);

  if (arch_archive_lock_revision (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      safe_printfmt (2, "arch_tag: unable to acquire revision lock (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, revision);
      exit (2);
    }

  if (arch_archive_put_log (&errstr, arch, version, prev_level, my_uid, txn_id, cooked_log))
    {
      safe_printfmt (2, "arch_tag: unable to send log message to archive (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, version);
      exit (2);
    }

  if (arch_archive_put_changeset (&errstr, arch, version, prev_level, my_uid, txn_id, this_level, tag_changeset))
    {
      safe_printfmt (2, "arch_tag: unable to send log message to archive (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, version);
      exit (2);
    }

  if (arch_archive_put_continuation (&errstr, arch, version, prev_level, my_uid, txn_id, continuation))
    {
      safe_printfmt (2, "arch_tag: unable to send tagged revision id to archive (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, revision);
      exit (2);
    }

  if (arch_revision_ready (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      safe_printfmt (2, "arch_tag: error sending tag to archive (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, revision);
      exit (2);
    }
  
  if (arch_archive_finish_revision (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      safe_printfmt (2, "arch_tag: unable to complete commit transaction (%s)\n    revision: %s/%s\n",
                     errstr, arch->name, version);
      exit (2);
    }

  {
    t_uchar * revision = 0;

    revision = str_alloc_cat_many (0, version, "--", this_level, str_end);

    arch_run_hook ("tag", "ARCH_ARCHIVE", arch->name, "ARCH_REVISION", revision, "ARCH_TAGGED_ARCHIVE", from_arch->name, "ARCH_TAGGED_REVISION", from_revision, (t_uchar*)0);

    lim_free (0, revision);
  }

  {
    t_uchar * dir = 0;
    t_uchar * tail = 0;

    dir = file_name_directory_file (0, tag_changeset);
    tail = file_name_tail (0, dir);

    invariant (!str_cmp_prefix (",,changeset-for-tag", tail));
    rmrf_file (dir);

    lim_free (0, dir);
    lim_free (0, tail);
  }

  lim_free (0, version);
  lim_free (0, this_level);
  lim_free (0, prev_revision);
  lim_free (0, prev_level);
  lim_free (0, continuation);
  lim_free (0, cooked_log);
  lim_free (0, my_uid);
  lim_free (0, txn_id);
  lim_free (0, tag_changeset);
}




static t_uchar *
arch_prepare_for_tag (t_uchar ** cooked_log_ret, struct arch_archive * arch, t_uchar * revision,
                      struct arch_archive * from_arch, t_uchar * from_revision,
                      t_uchar * raw_log)
{
  t_uchar * my_id = 0;
  time_t now;
  t_uchar * std_date = 0;
  t_uchar * human_date = 0;
  t_uchar * from_version = 0;
  int out_fd;
  rel_table merge_points = rel_table_nil;
  t_uchar * fqrevision = 0;
  t_uchar * cooked_log = 0;
  t_uchar * changeset_dir_path = 0; /* allocated and returned */
  t_uchar * changeset_tail = 0;
  t_uchar * changeset_path = 0;
  struct arch_make_changeset_report make_report = {0, };
  struct arch_changeset_report report = {rel_table_nil, };
  int log_fd = -1;
  t_uchar * log_loc = 0;
  t_uchar * log_id = 0;

  my_id = arch_my_id ();
  now = time(0);
  std_date = standard_date (now);
  human_date = pretty_date (now);

  from_version = arch_parse_package_name (arch_ret_package_version, 0, from_revision);

  merge_points = arch_archive_merge_points (from_arch, from_revision, 0, 0, 1);
  fqrevision = arch_fully_qualify (arch->name, revision);
  rel_add_records (&merge_points, rel_make_record_2_taking (rel_make_field_str ("!!!!!nothing-should-depend-on-this"), rel_make_field_str (fqrevision)), rel_record_null);
  rel_sort_table_by_field (0, merge_points, 1);
  rel_uniq_by_field (&merge_points, 1);
  arch_sort_table_by_name_field (0, merge_points, 1);

  out_fd = make_output_to_string_fd ();

  safe_printfmt (out_fd, "Revision: %s\n", revision);
  safe_printfmt (out_fd, "Archive: %s\n", arch->name);
  safe_printfmt (out_fd, "Creator: %s\n", my_id);
  safe_printfmt (out_fd, "Date: %s\n", human_date);
  safe_printfmt (out_fd, "Standard-date: %s\n", std_date);
  arch_print_log_list_header (out_fd, "New-patches", merge_points, 1);
  safe_printfmt (out_fd, "Continuation-of: %s/%s\n", from_arch->name, from_revision);
  if (raw_log)
    safe_printfmt (out_fd, "%s", raw_log);
  else
    safe_printfmt (out_fd, "Summary: tag of %s/%s\n\n(automatically generated log message)\n", from_arch->name, from_revision);

  cooked_log = string_fd_close (out_fd);

  changeset_dir_path = tmp_file_name (".", ",,changeset-for-tag");
  changeset_tail = str_alloc_cat (0, revision, ".patches");
  changeset_path = file_name_in_vicinity (0, changeset_dir_path, changeset_tail);
  rmrf_file (changeset_dir_path);
  safe_mkdir (changeset_dir_path, 0777);
  arch_make_empty_changeset (&make_report, &report, changeset_path);
  log_loc = arch_log_file (".", arch->name, revision);
  log_id = arch_log_file_id (arch->name, revision);
  log_fd = arch_changeset_add_file (0, &report, &make_report, changeset_path, log_loc, log_id);
  safe_printfmt (log_fd, "%s", cooked_log);
  safe_close (log_fd);
  arch_changeset_rewrite_indexes (changeset_path, &report);


  lim_free (0, my_id);
  lim_free (0, std_date);
  lim_free (0, human_date);
  lim_free (0, fqrevision);
  rel_free_table (merge_points);
  lim_free (0, changeset_tail);
  lim_free (0, changeset_dir_path);
  lim_free (0, log_loc);
  lim_free (0, log_id);
  arch_free_make_changeset_report_data (&make_report);
  arch_free_changeset_report_data (&report);

  *cooked_log_ret = cooked_log;
  return changeset_path;
}




/* tag: Tom Lord Tue May 27 17:37:04 2003 (tag.c)
 */
