/* archive-setup.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/arrays/ar.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/safe.h"
#include "tla/libawk/associative.h"
#include "tla/libarch/chatter.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/archive.h"
#include "tla/libarch/tag.h"
#include "tla/libarch/archive-cache.h"
#include "tla/libarch/archive-setup.h"


/* __STDC__ prototypes for static functions */
static struct arch_archive * find_archive (struct arch_archive ** archs,
                                           const t_uchar * name);



void
arch_setup_archive_simple (int chatter_fd,
                           const t_uchar * archive_spec,
                           const t_uchar * revision_spec)
{
  rel_table wants  = rel_table_nil;

  rel_add_records (&wants, rel_make_record_2_taking (rel_make_field_str (archive_spec), rel_make_field_str (revision_spec)), rel_record_null);
  arch_setup_archive (chatter_fd, wants, arch_archive_setup_no_tags, 0);

  rel_free_table (wants);
}

void
arch_setup_archive_simple_ext (int chatter_fd,
			       struct arch_archive * arch,
			       const t_uchar * revision_spec)
{
  rel_table wants = rel_table_nil;

  rel_add_records (&wants, rel_make_record_2_taking (rel_make_field_str (arch->name), rel_make_field_str (revision_spec)), rel_record_null);
  arch_setup_archive_ext (chatter_fd, wants, arch_archive_setup_no_tags, 0, arch);

  rel_free_table (wants);
}

void
arch_setup_archive (int chatter_fd, rel_table wants,
                    enum arch_archive_setup_tag_op tag_op,
                    int archive_cache)
{
  arch_setup_archive_ext (chatter_fd, wants, tag_op, archive_cache, NULL);
}

static void
arch_archive_populate_fqcategory_exists (struct arch_archive * arch, assoc_table * fqcategory_exists)
{
  int c;
  rel_table categories = arch_archive_categories (arch);

  for (c = 0; c < rel_n_records (categories); ++c)
    {
      t_uchar * cat = 0;

      cat = arch_fully_qualify (arch->name, rel_peek_str (categories, c, 0));
      assoc_set_taking (fqcategory_exists, rel_make_field_str (cat), rel_make_field_str ("yes"));

      lim_free (0, cat);
    }

  rel_free_table (categories);
}

void
arch_setup_archive_ext (int chatter_fd, rel_table wants,
                        enum arch_archive_setup_tag_op tag_op,
                        int archive_cache,
                        struct arch_archive * initial_arch)
{
  t_uchar * errstr = 0;
  struct arch_archive ** archs = 0;
  assoc_table fqcategories_checked = 0;
  assoc_table fqbranches_checked = 0;
  assoc_table fqcategory_exists = 0;
  assoc_table fqbranch_exists = 0;
  assoc_table fqversion_exists = 0;
  int x;

  if (initial_arch)
    *(struct arch_archive **)ar_push ((void **)&archs, 0, sizeof (initial_arch)) = initial_arch;

  for (x = 0; x < rel_n_records (wants); ++x)
    {
      const t_uchar * archive;
      struct arch_archive * arch = 0;
      const t_uchar * spec;
      t_uchar * category = 0;
      t_uchar * fqcategory = 0;

      archive = rel_peek_str (wants, x, 0);

      arch = find_archive (archs, archive);

      if (!arch)
        {
          arch = arch_archive_connect (archive, 0);
          *(struct arch_archive **)ar_push ((void **)&archs, 0, sizeof (arch)) = arch;

          arch_archive_populate_fqcategory_exists (arch, &fqcategory_exists);
        }
  
      if (!fqcategory_exists)
        arch_archive_populate_fqcategory_exists (arch, &fqcategory_exists);

      spec = rel_peek_str (wants, x, 1);

      category = arch_parse_package_name (arch_ret_category, 0, spec);
      fqcategory = arch_fully_qualify (archive, category);

      if ((arch->type != arch_archive_baz) && !assoc_get_str_taking (fqcategory_exists, rel_make_field_str (fqcategory)))
        {
          arch_chatter (chatter_fd, "* creating category %s/%s\n", archive, category);
          if (arch_make_category (&errstr, arch, category))
            {
              safe_printfmt (2, "archive-setup: error making category %s/%s (%s)\n", archive, category, errstr);
              exit (1);
            }
          assoc_set_taking (&fqcategory_exists, rel_make_field_str (fqcategory), rel_make_field_str ("yes"));
        }

      if (str_cmp (spec, category))
        {
          if (arch_valid_package_name (spec, arch_no_archive, arch_req_package, 1))
            {
              t_uchar * branch = 0;
              t_uchar * fqbranch = 0;

              branch = arch_parse_package_name (arch_ret_package, 0, spec);
              fqbranch = arch_fully_qualify (archive, branch);

              if (!assoc_get_str_taking (fqcategories_checked, rel_make_field_str (fqcategory)))
                {
                  rel_table branches  = rel_table_nil;
                  int b;

                  branches = arch_archive_branches (arch, category);
                  for (b = 0; b < rel_n_records (branches); ++b)
                    {
                      t_uchar * bran = 0;

                      bran = arch_fully_qualify (archive, rel_peek_str (branches, b, 0));
                      assoc_set_taking (&fqbranch_exists, rel_make_field_str (bran), rel_make_field_str ("yes"));

                      lim_free (0, bran);
                    }

                  rel_free_table (branches);
                }

              if ((arch->type != arch_archive_baz) && !assoc_get_str_taking (fqbranch_exists, rel_make_field_str (fqbranch)))
                {
                  arch_chatter (chatter_fd, "* creating branch %s/%s\n", archive, branch);
                  if (arch_make_branch (&errstr, arch, branch))
                    {
                      safe_printfmt (2, "archive-setup: error making branch %s/%s (%s)\n", archive, branch, errstr);
                      exit (1);
                    }
                  assoc_set_taking (&fqbranch_exists, rel_make_field_str (fqbranch), rel_make_field_str ("yes"));
                }

              if (arch_valid_package_name (spec, arch_no_archive, arch_req_version, 0))
                {
                  t_uchar * version = 0;
                  t_uchar * fqversion = 0;

                  version = arch_parse_package_name (arch_ret_package_version, 0, spec);
                  fqversion = arch_fully_qualify (archive, version);

                  if (!assoc_get_str_taking (fqbranches_checked, rel_make_field_str (fqbranch)))
                    {
                      rel_table versions  = rel_table_nil;
                      int v;

                      versions = arch_archive_versions (arch, branch);
                      for (v = 0; v < rel_n_records (versions); ++v)
                        {
                          t_uchar * vers = 0;
                          vers = arch_fully_qualify (archive, rel_peek_str (versions, v, 0));
                          assoc_set_taking (&fqversion_exists, rel_make_field_str (vers), rel_make_field_str ("yes"));
                          lim_free (0, vers);
                        }

                      rel_free_table (versions);
                    }

                  if (!assoc_get_str_taking (fqversion_exists, rel_make_field_str (fqversion)))
                    {
                      arch_chatter (chatter_fd, "* creating version %s/%s\n", archive, version);
                      if (arch_make_version (&errstr, arch, version))
                        {
                          safe_printfmt (2, "archive-setup: error making version %s/%s (%s)\n", archive, version, errstr);
                          exit (1);
                        }
                      assoc_set_taking (&fqversion_exists, rel_make_field_str (fqversion), rel_make_field_str ("yes"));
                    }

                  if (tag_op != arch_archive_setup_no_tags)
                    {
                      rel_table has_revisions  = rel_table_nil;

                      const t_uchar * tag_from_archive;
                      const t_uchar * tag_from_rev_spec;

                      struct arch_archive * from_arch = 0;
                      t_uchar * tag_from_version = 0;
                      rel_table from_has_revisions  = rel_table_nil;

                      arch_chatter (chatter_fd, "* creating tag in %s/%s\n", archive, version);
                      has_revisions = arch_archive_revisions (arch, version, 0);

                      tag_from_archive = rel_peek_str (wants, x, 2);
                      tag_from_rev_spec = rel_peek_str (wants, x, 3);

                      from_arch = find_archive (archs, tag_from_archive);
                      if (!from_arch)
                        {
                          from_arch = arch_archive_connect (tag_from_archive, 0);
                          *(struct arch_archive **)ar_push ((void **)&archs, 0, sizeof (arch)) = from_arch;
                        }

                      tag_from_version = arch_parse_package_name (arch_ret_package_version, 0, tag_from_rev_spec);
                      from_has_revisions = arch_archive_revisions (from_arch, tag_from_version, 0);

                      switch (tag_op)
                        {
                        default:
                          {
                            panic ("archive-setup.c internal error");
                            break;
                          }
                        case arch_archive_setup_make_base0_tag:
                          {
                            if (rel_n_records (has_revisions))
                              arch_chatter (chatter_fd, " ...skipping, base-0 already exists");
                            else
                              {
                                t_uchar * tag_revision = 0;
                                t_uchar * tag_from_revision = 0;

                                tag_revision = str_alloc_cat (0, version, "--base-0");

                                if (arch_valid_package_name (tag_from_rev_spec, 0, arch_req_patch_level, 0))
                                  tag_from_revision = arch_parse_package_name (arch_ret_non_archive, 0, tag_from_rev_spec);
                                else if (rel_n_records (from_has_revisions))
                                  tag_from_revision = str_alloc_cat_many (0, tag_from_version, "--", rel_peek_str (from_has_revisions, rel_n_records (from_has_revisions) - 1, 0), str_end);

                                if (tag_from_revision)
                                  {
                                    arch_tag (chatter_fd, arch, tag_revision, from_arch, tag_from_revision, 0);

                                    if (archive_cache)
                                      {
                                        arch_chatter (chatter_fd, "* archive caching %s/%s\n", archive, tag_revision);
                                        arch_archive_cache (chatter_fd, arch, archive, tag_revision, 0);
                                      }
                                  }

                                lim_free (0, tag_from_revision);
                                lim_free (0, tag_revision);
                              }

                            break;
                          }
                        }

                      rel_free_table (has_revisions);
                      lim_free (0, tag_from_version);
                      rel_free_table (from_has_revisions);
                    }

                  lim_free (0, version);
                  lim_free (0, fqversion);
                }

              lim_free (0, branch);
              lim_free (0, fqbranch);
            }
        }
      lim_free (0, category);
      lim_free (0, fqcategory);
    }

  for (x = 0; x < ar_size ((void *)archs, 0, sizeof (*archs)); ++x)
    if (archs[x] != initial_arch)
      arch_archive_close (archs[x]);

  ar_free ((void **)&archs, 0);
  free_assoc_table (fqcategories_checked);
  free_assoc_table (fqbranches_checked);
  free_assoc_table (fqcategory_exists);
  free_assoc_table (fqbranch_exists);
  free_assoc_table (fqversion_exists);
}


static struct arch_archive *
find_archive (struct arch_archive ** archs,
              const t_uchar * name)
{
  int x;

  for (x = 0; x < ar_size ((void *)archs, 0, sizeof (*archs)); ++x)
    if (!str_cmp (name, archs[x]->name))
      return archs[x];

  return 0;
}



/* tag: Tom Lord Wed Jun 11 11:58:43 2003 (archive-setup.c)
 */
