/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
   Copyright (c) 2013 D. Richard Hipp
  
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Simplified BSD License (also
   known as the "2-Clause License" or "FreeBSD License".)
  
   This program is distributed in the hope that it will be useful,
   but without any warranty; without even the implied warranty of
   merchantability or fitness for a particular purpose.
  
   Author contact information:
     drh@hwaci.com
     http://www.hwaci.com/drh/
  
  *****************************************************************************
   This file implements a basic 'ls' for in-repo content
   (not yet filesystem-level).
*/

#include "fossil-scm/fossil-cli.h" /* Fossil App mini-framework */
/* #include "fossil-scm/fossil-internal.h" */

static void fcli_local_help(){
  printf("Usage:\n\t%s [options]\n\n", fcli.appName);

  puts("Lists all files in a repository. Currently does no introspection "
       "vs. the local checkout. "
       "\nOptions:\n");

  puts("\t-g|--glob GLOB_LIST list only filenames matching the given "
       "list of space-or-comma-separated glob pattern All patterns must "
       "be provided as a single string argument, so be sure to quote them "
       "if your shell might resolve them as wildcards.\n");

  puts("\t-v|--invert Inverts the effect of --glob to mean list files "
       "whose names do NOT match the given blob.\n");

  puts("\t--own|-o lists only files actually contained in the "
       "given version's manifest. The result will differ only "
       "for delta manifests.\n");
  
  puts("\t--version|-v=VERSION (uuid or symbolic name). "
       "Default is 'current' if there is an opened checkout, "
       "else 'trunk'.\n");

  puts("\t--verbose|-V enables more details.\n");
}

static struct LsApp {
  char * glob;
  fsl_list globs;
  char invertGlob;
  
} LsApp = {
  NULL,
  fsl_list_empty_m,
  0/*invertGlob*/
};


static char ls_matches(char const * name){
  if(!LsApp.globs.used) return 1;
  else{
    char const rc = fsl_glob_list_matches(&LsApp.globs, name) ? 1 : 0;
    return LsApp.invertGlob ? !rc : rc;
  }
}
/**
   A fsl_card_F_visitor_f() implementation which outputs
   state from fc to the fossil output channel.
*/
static int ls_F_card_v(fsl_card_F const * fc, void * state){
  char show;
  if(!fc->uuid) return 0 /* was removed in this manifest */;
  show = ls_matches(fc->name);
  if(show){
    char perm;
    if(FSL_FILE_PERM_EXE & fc->perm) perm = 'x';
    else if(FSL_FILE_PERM_LINK & fc->perm) perm = 'L';
    else perm = '-';
    if(fcli.verbose){
      f_out("%-8"FSL_ID_T_PFMT,
            fsl_uuid_to_rid(fcli.f, fc->uuid));
    }
    f_out("%.*s %c %s\n", 12, fc->uuid, perm, fc->name);
  }
  return 0;
}

int main(int argc, char * const * argv ){
  int rc = 0;
  char * lsVersion = NULL;
  fsl_cx * f;
  fsl_deck deck = fsl_deck_empty;
  fsl_deck * d = &deck;
  char onlyFromSelf = 0;

  fcli.appHelp = fcli_local_help;
  rc = fcli_setup(argc, argv);
  if(FSL_RC_BREAK==rc) /* --help */ return 0;
  else if(rc) goto end;


  /* Set up/validate args... */
  f = fcli.f;
  if(!fsl_cx_db_repo(f)){
    rc = fcli_err_set(FSL_RC_NOT_A_REPO,
                      "Requires a repository db. See --help.");
    goto end;
  }
  onlyFromSelf = fcli_flag2("o", "own", NULL);
  fcli_flag2("g","glob", &LsApp.glob);
  if(LsApp.glob){
    fsl_glob_list_parse(&LsApp.globs, LsApp.glob);
  }

  LsApp.invertGlob = fcli_flag2("v","invert", NULL);

  if(!fcli_flag_or_arg("v","version", &lsVersion)){ /* this flag check MUST come last! */
    lsVersion = fsl_strdup(fsl_cx_db_checkout(f) ? "current" : "trunk");
  }

  if(fcli_has_unused_flags(0)) goto end;

  /* Find/load the checkin... */
  rc = fsl_deck_load_sym(f, d, lsVersion, FSL_CATYPE_CHECKIN);
  if(rc) goto end;


  /* Output the list...*/
  f_out("File list from manifest version '%s' [%.*s] "
        "(RID %"FSL_ID_T_PFMT")...\n",
        lsVersion, 12, d->uuid, (fsl_id_t)d->rid);
 if(d->B.uuid){
    f_out("This is a delta manifest from baseline [%.*s].%s\n",
          12, d->B.uuid,
          onlyFromSelf
          ? " Only listing differences from the baseline version."
          : "");
  }
  if(fcli.verbose) f_out("RID     ");
  f_out("%-12s P Name\n", "UUID");
  rc = fsl_deck_F_foreach(d, onlyFromSelf ? 0 : 1,
                          ls_F_card_v, NULL);
  
  end:
  fsl_free(LsApp.glob);
  fsl_glob_list_clean(&LsApp.globs);
  fsl_free(lsVersion);
  fsl_deck_finalize(d);
  rc = fcli_end_of_main(rc);
  return rc;
}
