/* conflict-handling.c
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2005 Canonical Ltd.
 * Original Author: James Blackwell <jblack@gnuarch.org>
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */

#include "hackerlab/bugs/panic.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/vu/safe.h"
#include "libarch/arch.h"
#include "libarch/conflict-handling.h"
#include "libarch/pfs.h"
#include "libarch/project-tree.h"
#include "libarch/proj-tree-lint.h"
#include "libawk/relational.h"
#include "libfsutils/rmrf.h"
#include "po/gettext.h"



#define conflicts_exist_string "+rejects-list"
#define old_conflicts_exist_string "=rejects-exist"


static int arch_tree_save_conflicts (arch_project_tree_t * tree, rel_table file_list);
static int load_conflict_file (t_uchar * program_name,
                               arch_project_tree_t * tree,
                               rel_table * conflicts);
static int arch_tree_add_conflicts (arch_project_tree_t * tree, rel_table new_files);




static int
load_conflict_file (t_uchar * program_name,
                    arch_project_tree_t * tree,
                    rel_table * return_conflicts)
{
  int in_fd;
  int errn;
  t_uchar * conflict_file;
  t_uchar * arch_tree;
  rel_table conflicts;

  arch_tree = arch_tree_ctl_dir(tree->root);

  /* We better have an empty tree */
  if (! arch_tree)
    return 1;


  conflict_file = file_name_in_vicinity (0,
                                         arch_tree,
                                         conflicts_exist_string);
  
  in_fd = vu_open (&errn, conflict_file, O_RDONLY  , 0);

  if (in_fd == -1)
    {
      return errn;
    }
  
  conflicts = rel_read_pika_unescape_iso8859_1_table (in_fd, 2, program_name,
                              conflicts_exist_string);
  safe_close (in_fd);
  rel_append_x(return_conflicts, conflicts);

  rel_free_table(conflicts);
  return 0;

}

enum arch_conflict_problems
arch_tree_unconflict_files (t_uchar * program_name,
                            arch_project_tree_t * tree,
                         rel_table files)
{
  int errn;
  enum arch_conflict_problems retval;
  t_uchar * conflict_file;
  t_uchar * arch_tree;
  rel_table new_conflicts = NULL;
  rel_table conflicts = NULL;

  invariant (tree->root != NULL);
  invariant (rel_n_records (files));

  /* We need to gather some information. Where the arch tree is,
   * the name of the conflict file. We also need to load the conflict file
   */
  arch_tree = arch_tree_ctl_dir(tree->root);
  if (! arch_tree)
    return CONFLICTS_TREE_PROBLEM;

  conflict_file = file_name_in_vicinity (0, arch_tree, conflicts_exist_string);

  errn = load_conflict_file (program_name, tree, & conflicts);
  if (errn)
    /* FIXME LEAKS */
    return CONFLICTS_FS_PROBLEM;

  rel_sort_table_by_field (0, conflicts, 0);
  rel_sort_table_by_field (0, files,0);
  new_conflicts = rel_join (1, rel_join_output (1,0,  1,1,  -1),
                            0, 0, conflicts, files);
  
  /* One possibility: The user fixed everything */
  if ( rel_n_records(new_conflicts) == 0)
   {
      retval = CONFLICTS_NO_LONGER_CONFLICTED;

      rmrf_file(conflict_file);
   }

  /* Another possibility: They fixed NOTHING */
  else if (rel_n_records(new_conflicts) == rel_n_records(conflicts))
   {
      retval = CONFLICTS_NOTHING_CHANGED;
   }

  /* Most likely, they fixed something */
  else
    {
      int num_conflicts;
        
      retval = CONFLICTS_REMOVED;

      num_conflicts = arch_tree_save_conflicts (tree, new_conflicts);
      if (num_conflicts < 0)
        safe_printfmt(2, _("Unable to save new conflicts file"));

      safe_printfmt(2, _("%d conflicts remain in this tree\n"),
                    num_conflicts);
    }
    
    lim_free(0, conflict_file);
    lim_free(0, arch_tree);
    
    rel_free_table(conflicts);
    rel_free_table(new_conflicts);
    return retval;
}

int
arch_tree_show_conflicts (t_uchar * program_name,
                          arch_project_tree_t * tree,
                          rel_table * output)
{
  rel_table conflicts = 0;
  int errn;
 
  int loop;
  
  errn = load_conflict_file (program_name,
                             tree,
                             &conflicts);
  if (errn)
    return errn;


  for ( loop = 0; loop < rel_n_records(conflicts); loop++)
    {
      t_uchar * after_dotslash;
      
      after_dotslash = conflicts[loop][0]+2;

      rel_add_records(output,
                      rel_make_record(after_dotslash, 0),
                      0);
    }
  return 0;
}

int
arch_tree_add_conflicts (arch_project_tree_t * tree, rel_table new_conflicts)
{

  int result; 
  t_uchar * arch_tree;
  t_uchar * conflict_file;

  rel_table old_conflicts = 0;
  rel_table final_conflicts = 0;
  rel_table merge_conflicts = 0;

  arch_tree = arch_tree_ctl_dir (tree->root);

  if (! arch_tree)
    {
      return -1;
    }

  conflict_file = file_name_in_vicinity (0, arch_tree, conflicts_exist_string);

  result = load_conflict_file ("arch_tree_add_conflicts", tree,
                               &old_conflicts);

  if ( rel_n_records (old_conflicts) == 0)
    {
      /* We only have new conflict files */
      final_conflicts = new_conflicts;
    }
  else
    {
      /* we have existing conflicts. Now we have to merge two tables into one,
       * avoiding duplicates
       */
      int x;
      int y;

      /* First, lets hit the existing ones. Though duplicates shouldn't 
       * exist, it doesn't hurt to be extra careful making sure
       */
      for (x = 0; x < rel_n_records(old_conflicts); x++)
        {
          int found = 0;
          for (y = 0; y < rel_n_records(merge_conflicts); y++)
            /*Compare against merge confclits to see if its there yet
            */
            {
              if (0 == str_cmp(old_conflicts[x][0], merge_conflicts[y][0]))
                found = 1;
            }
          if (! found)
            rel_add_records (&merge_conflicts,
                             rel_copy_record (old_conflicts[y]) ,0 );
        }

      /* Now, we'll compare the new conflicts against the merge conflicts.
       * If we don't have it in there, then add it
       */
      for (x = 0; x < rel_n_records(new_conflicts); x++)
        {
          int found = 0;
          for (y = 0; y < rel_n_records(merge_conflicts); y++)
            {
              /*Compare against merge confclits to see if its there yet
              */
              if (0 == str_cmp (new_conflicts[x][0], merge_conflicts[y][0]))
                found = 1;
            }
          if ( ! found)
            rel_add_records (&merge_conflicts,
                             rel_copy_record (new_conflicts[y]),0);
        }
      final_conflicts = merge_conflicts;
    }

  if (rel_n_records (final_conflicts))
      result = arch_tree_save_conflicts (tree, final_conflicts);
  else 
      result = 0;

  lim_free (0, conflict_file);
  lim_free (0, arch_tree);
  rel_free_table (old_conflicts);
  rel_free_table (merge_conflicts);

  return result;
}

int
arch_tree_save_conflicts (arch_project_tree_t * tree, rel_table file_list)
{
  t_uchar * arch_tree = 0;

  arch_tree = arch_tree_ctl_dir(tree->root);

  if (! arch_tree)
    return -1;

  else
    {
      int out_fd;
      int errn;
      t_uchar *  conflict_file = 0;

      conflict_file = file_name_in_vicinity (0, arch_tree, conflicts_exist_string);

      out_fd = vu_open (&errn, conflict_file, O_WRONLY|O_TRUNC|O_CREAT , 0666);
      rel_print_pika_escape_iso8859_1_table (out_fd, arch_escape_classes, file_list);

      safe_close (out_fd);

      lim_free (0, conflict_file);
    }

  lim_free (0, arch_tree);

  return rel_n_records (file_list);
}

void
arch_tree_note_conflicts (arch_project_tree_t * tree,
                          struct arch_apply_changeset_report * r)
{
  int num_conflicts;
  rel_table all_conflicts = 0;

  rel_append_x ( &all_conflicts, r->conflict_files);
  rel_append_x ( &all_conflicts, r->conflict_dirs);
  rel_append_x ( &all_conflicts, r->metadata_conflict_files);
  rel_append_x ( &all_conflicts, r->metadata_conflict_dirs);

  num_conflicts = arch_tree_add_conflicts (tree, all_conflicts);
  if (num_conflicts)
    {
      safe_printfmt(2, "****************************************************\n");
      safe_printfmt(2, "%d conflicted items in this tree. Please\n", num_conflicts);
      safe_printfmt(2, "resolve each conflict with baz \"resolved 'filename'\"\n");
      safe_printfmt(2, "****************************************************\n");
    }


}

void
arch_tree_ensure_no_conflicts (arch_project_tree_t * tree)
{
  if ( arch_tree_conflicts_exist(tree))
    {
      safe_printfmt(2, "Sorry. This command may not be used when the tree is in a \n"
                     " conflicted state. Please resolve conflicts first, and then \n"
                     " run baz resolved\n");
      exit(2);
    }
}

int
arch_tree_conflicts_exist (arch_project_tree_t * tree)
{
  t_uchar * arch_tree = 0;
  int answer = 0;

  arch_tree = arch_tree_ctl_dir(tree->root);

  if (! arch_tree)
     /* Whoah! No arch tree?!?! */
     answer= -1;
  else
    {
      t_uchar *  conflict_file = 0;

      conflict_file = file_name_in_vicinity (0, arch_tree, conflicts_exist_string);

      answer = safe_access (conflict_file, F_OK) ? 0 : 1;


      lim_free (0, conflict_file);
    }

  lim_free (0, arch_tree);

  return answer;
}

int
arch_tree_clear_conflicts (arch_project_tree_t * tree)
{
  t_uchar * arch_tree = 0;
  int answer = 0;

  arch_tree = arch_tree_ctl_dir(tree->root);

  if (! arch_tree)
     /* Whoah! No arch tree?!?! */
     answer= -1;
  else
    {
      t_uchar *  conflict_file = 0;
      conflict_file = file_name_in_vicinity (0, arch_tree, conflicts_exist_string);
      rmrf_file (conflict_file);
      lim_free (0, conflict_file);
      
      conflict_file = file_name_in_vicinity (0, arch_tree, old_conflicts_exist_string);
      rmrf_file (conflict_file);
      lim_free (0, conflict_file);
    }

  lim_free (0, arch_tree);

  return answer;
}


/* tag: James Blackwell Mon Jan 31 00:22:09 EST 2005 (conflict-handling.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1