/* 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, t_uchar * name);



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

  rel_add_records (&wants, rel_make_record (archive_spec, revision_spec, 0), 0);
  arch_setup_archive (chatter_fd, wants, arch_archive_setup_no_tags, 0);

  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)
{
  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;

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

      archive = wants[x][0];

      arch = find_archive (archs, archive);

      if (!arch)
        {
          rel_table categories = 0;
          int c;

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

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

              cat = arch_fully_qualify (archive, categories[c][0]);
              assoc_set (&fqcategory_exists, cat, "yes");

              lim_free (0, cat);
            }

          rel_free_table (categories);
        }

      spec = wants[x][1];

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

      if (!assoc_ref (fqcategory_exists, 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 (&fqcategory_exists, fqcategory, "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_ref (fqcategories_checked, fqcategory))
                {
                  rel_table branches = 0;
                  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, branches[b][0]);
                      assoc_set (&fqbranch_exists, bran, "yes");

                      lim_free (0, bran);
                    }

                  rel_free_table (branches);
                }

              if (!assoc_ref (fqbranch_exists, 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 (&fqbranch_exists, fqbranch, "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_ref (fqbranches_checked, fqbranch))
                    {
                      rel_table versions = 0;
                      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, versions[v][0]);
                          assoc_set (&fqversion_exists, vers, "yes");
                          lim_free (0, vers);
                        }

                      rel_free_table (versions);
                    }

                  if (!assoc_ref (fqversion_exists, 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 (&fqversion_exists, fqversion, "yes");
                    }

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

                      t_uchar * tag_from_archive;
                      t_uchar * tag_from_rev_spec;

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

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

                      tag_from_archive = wants[x][2];
                      tag_from_rev_spec = 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 (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 (from_has_revisions)
                                  tag_from_revision = str_alloc_cat_many (0, tag_from_version, "--", 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)
    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, 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)
 */
