/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
  Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt

  SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  SPDX-FileCopyrightText: 2021 The Libfossil Authors
  SPDX-ArtifactOfProjectName: Libfossil
  SPDX-FileType: Code
*/
/**
   This is a test app demonstrating creating a repository and checking
   in files without an associated checkout.
*/
#ifdef NDEBUG
/* Force assert() to always be in effect. */
#undef NDEBUG
#endif
#include "libfossil.h"

// Only for testing/debugging..
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)

// Global app state.
struct App_ {
  char const * repoDbName;
  bool addEmptyCommit;
  bool addRCard;
} App = {
"_ciwoco.f",
true/*addEmptyCommit*/,
true/*addRCard*/
};

static int repo_create(bool addEgg){
  int rc;
  fsl_cx * const f = fcli_cx();
  fsl_repo_create_opt cOpt = fsl_repo_create_opt_empty;
  if(addEgg){
    cOpt.commitMessage =
      "This is a repo. There are many like it "
      "but this one is mine.";
  }else{
    cOpt.commitMessage = NULL;
  }
  cOpt.filename = App.repoDbName;
  fsl_file_unlink(cOpt.filename);
  rc = fsl_repo_create(f, &cOpt);
  assert(0==rc);
  assert(fsl_cx_db_repo(f));
  return rc;
}

static int setup_deck(fsl_deck *d, char const * msg){
  double const julian = true
    ? fsl_db_julian_now(fsl_cx_db_repo(d->f))
    /* ^^^^ ms precision */
    : fsl_julian_now() /* seconds precision */;
  int rc = fsl_deck_D_set(d, julian);
  assert(0==rc);
  rc = fsl_deck_C_set(d, msg, -1);
  assert(0==rc);
  rc = fsl_deck_U_set(d, "ciwoco");
  assert(0==rc);
  return rc;
}

static int do_demo(void){
  int rc = 0;
  fsl_cx * const f = fcli_cx();
  fsl_buffer content = fsl_buffer_empty;
  fsl_deck d = fsl_deck_empty;
  char const *fname = 0;
  rc = repo_create(App.addEmptyCommit);
  if(rc) goto end;
  f_out("(Re)created repo: %s\n", fsl_cx_db_file_repo(f, NULL));
  if(App.addEmptyCommit){
    f_out("Empty initial commit was added.\n");
  }else{
    f_out("No empty initial commit was created.\n");
  }
  assert(fsl_needs_repo(f));

  fsl_cx_flag_set(f, FSL_CX_F_CALC_R_CARD, App.addRCard);
  f_out("R-card generation is %s\n", App.addRCard ? "ON" : "OFF");

  rc = fsl_cx_transaction_begin(f);
  if(rc) goto end;

  //////////////////////////////////////////////////////////////
  // Step 1: initialize our deck...
  if(App.addEmptyCommit){
    rc = fsl_deck_load_sym(f, &d, "trunk", FSL_SATYPE_CHECKIN);
    assert(0==rc);
    assert(f==d.f);
    f_out("Deriving from initial trunk checkin #%"FSL_ID_T_PFMT"\n", d.rid);
    rc = fsl_deck_derive(&d);
    assert(0==rc);
  }else{
    f_out("Creating initial commit with files.\n");
    fsl_deck_init(f, &d, FSL_SATYPE_CHECKIN);
    /* If we don't set a branch, we cannot resolve the checkins
       via a branch name! */
    rc = fsl_deck_branch_set(&d, "trunk");
    assert(0==rc);
  }

  //////////////////////////////////////////////////////////////
  // Step 2: set up some commonly-required cards...
  rc = setup_deck(&d, "Files added w/o checkout.");
  assert(0==rc);

  //////////////////////////////////////////////////////////////
  // Step 3: add some files...
  char const * fnames[] = {
    "f-test-ciwoco.c",
    "GNUmakefile",
    NULL
  };
  for( int i = 0; (fname = fnames[i]); ++i ){
    rc = fsl_buffer_fill_from_filename(&content, fname); 
    assert(0==rc);
    rc = fsl_deck_F_set_content(&d, fname, &content,
                                FSL_FILE_PERM_REGULAR, NULL);
    assert(0==rc);
    f_out("Added file: %s\n", fname);
  }

  //////////////////////////////////////////////////////////////
  // Step 4: save the deck...
  rc = fsl_deck_save(&d, false);
  assert(0==rc);
  f_out("Saved checkin #%"FSL_ID_T_PFMT"\n", d.rid);

  //////////////////////////////////////////////////////////////
  // Step 5: ...
  f_out("Now we'll try again so we can ensure that deltaing "
        "of parent file content works.\n");
  fsl_deck_derive(&d);
  setup_deck(&d, "Modified GNUmakefile.");
  fname = "GNUmakefile";
  rc = fsl_buffer_fill_from_filename(&content, fname);
  assert(0==rc);
  rc = fsl_buffer_append(&content,
                         "\n# This is an edit. There are many "
                         "like it, but this one is mine.\n", -1);
  assert(0==rc);
  rc = fsl_deck_F_set_content(&d, fname, &content,
                              FSL_FILE_PERM_REGULAR, NULL);
  assert(0==rc);
  f_out("Added file: %s\n", fname);
  rc = fsl_deck_save(&d, false);
  assert(0==rc);
  f_out("Saved checkin #%"FSL_ID_T_PFMT"\n", d.rid);
  f_out("You can confirm that the previous file version is delta'd "
        "by running:\n    f-acat -R %s --raw rid:X\n"
        "where X is the lowest-numbered entry in this list:\n",
        App.repoDbName);
  fsl_db_each( fsl_cx_db(f), fsl_stmt_each_f_dump, NULL,
               "SELECT m.fid FROM mlink m, filename f "
               "WHERE m.fnid=f.fnid and f.name=%Q",
               fname);

  assert(fsl_cx_transaction_level(f)>0);
  rc = fsl_cx_transaction_end(f, false);
  end:
  if(fsl_cx_transaction_level(f)){
    fsl_cx_transaction_end(f, true);
  }
  fsl_deck_finalize(&d);
  fsl_buffer_clear(&content);
  if(!rc){
    f_out("Results are in repo file %s\n", App.repoDbName);
  }
  return rc;
}

int main(int argc, const char * const * argv ){
  /**
     Set up flag handling, which is used for processing
     basic CLI flags and generating --help text output.
  */
  const fcli_cliflag FCliFlags[] = {
    FCLI_FLAG_BOOL_INVERT("e", "empty", &App.addEmptyCommit,
                          "If set, do not create the initial "
                          "empty checkin."),
    FCLI_FLAG_BOOL_INVERT("r", "no-r-card", &App.addRCard,
                          "If set, do not add an R-card to commits."),
    fcli_cliflag_empty_m // list MUST end with this (or equivalent)
  };
  const fcli_help_info FCliHelp = {
    "A demo of creating a new repo and checking in files "
    "without a checkout.",
    NULL, // very brief usage text, e.g. "file1 [...fileN]"
    NULL // optional callback which outputs app-specific help
  };
  fcli.clientFlags.checkoutDir = NULL; // same effect as global -C flag.
  int rc = fcli_setup_v2(argc, argv, FCliFlags, &FCliHelp);
  if(rc) goto end;
  else if((rc=fcli_has_unused_args(false))) goto end;
  else if(fsl_cx_db_repo(fcli_cx())){
    rc = fcli_err_set(FSL_RC_MISUSE,
                      "This app must be started WITHOUT a repo/checkout.");
    goto end;
  }

  rc = do_demo();
  end:
  return fcli_end_of_main(rc);
}
