/* -*- 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 Stephan Beal (https://wanderinghorse.net).

  Derived heavily from previous work:

  Copyright (c) 2013 D. Richard Hipp (https://www.hwaci.com/drh/)


  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.

  *****************************************************************************
  This file implements the code to checkout a Fossil repository.
*/

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

#include <errno.h>
#include <sys/stat.h>
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)

static int verbose = 0;

typedef struct {
  int	extracted;
  int	kept;
  bool quiet;
} extract_state;

static int
fsl_repo_checkout_f_my(fsl_repo_checkout_state const *coState)
{
  enum {DATE_SHORT = 16};
  fsl_repo_extract_state const * xs = coState->xState;
  extract_state  *state = (extract_state *)coState->callbackState;
  static char    tbuf[DATE_SHORT];
  char mode = '?';
  if(coState->fileWasWritten) {
    mode = '+';
    ++state->extracted;
  }else{
    mode = '!';
    ++state->kept;
  }
  if(!state->quiet){
    fsl_strftime_unix(tbuf, DATE_SHORT, "%d %b %Y", coState->mtime, 1);
    f_out("[%c] %8"FSL_SIZE_T_PFMT"   %s  %s\n",
          mode, coState->size, tbuf, xs->fc->name);
  }
  return 0;
}

int
main(int argc, char const * const *argv)
{
  fsl_db	        *db = 0;
  extract_state	         ex = {0,0,0};
  fsl_cx	        *f = NULL;
  fsl_id_t               prevId = 0, rid = 0;
  const char	        *sym = NULL;
  bool	                 force = false, keep = false, manifest = false,
    mtime = false, q = false, doRm = true;
  int	                 rc = 0;
  fcli_cliflag	         fcli_flags[] = {
    FCLI_FLAG_BOOL("f", "force", &force, "Continue with the checkout "
     "irrespective of any unsaved changes in the current checkout."),
    FCLI_FLAG_BOOL(0, "keep", &keep, "Only checkout files from the requested "
     "<version> that do not have a file of the same name already present on "
     "disk. Files with the same name as those from the requested <version> will"
     " remain unmodified irrespective of whether their content is consistent "
     "with that of the requested <version>. In such a case, the checkout will "
     "immediately be in a changed state, which 'f-status' will report."),
    FCLI_FLAG_BOOL(0, "manifest", &manifest,
     "Only modify the manifest and manifest.uuid files."),
    FCLI_FLAG_BOOL("q", "quiet", &q,
     "Suppress non-error output unless --verbose is used."),
    FCLI_FLAG_BOOL(0, "setmtime", &mtime, "Set timestamps of all files to that "
     "of the last check-in in which they were modified (i.e., manifest time)."),
    FCLI_FLAG_BOOL_INVERT(0,"no-rm", &doRm,
                          "Do not delete files which were removed between the "
                          "original and new checkout versions."),
    fcli_cliflag_empty_m
  };
  fcli_help_info	 fcli_help = {
    "Change the current checkout to the requested <version> or to the tip of "
    "the trunk if no <version> is specified.\n",
    "[<version>]\n",
    NULL
  };

  fcli.cliFlags = fcli_flags;
  fcli.appHelp = &fcli_help;
  rc = fcli_setup(argc, argv);
  if (rc == FCLI_RC_HELP)
    return 0;
  else if (rc)
    goto end;

  f = fcli.f;
  db = fsl_needs_checkout(f);
  if(!db){
    goto end;
  }

  verbose = fcli_is_verbose();
  if(!verbose){
    verbose = !q;
  }

  if(fcli_has_unused_flags(0)){
    goto end;
  }

  rc = fsl_cx_transaction_begin(f);
  if(rc) goto end;
  fsl_checkout_version_info(f, &prevId, NULL);
  fsl_checkout_changes_scan(f);
  if(!force && fsl_checkout_has_changes(f)) {
    fcli_err_set(FSL_RC_ALREADY_EXISTS,
                 "The current checkout contains unsaved changes.");
    goto end;
  }
  
  if(force){
    fsl_checkout_clear_db(f);
  }

  if(!(sym = fcli_next_arg(1))){
    char * mainBranch = fsl_config_get_text(f, FSL_CONFDB_REPO,
                                            "main-branch", 0);
    if(mainBranch){
      fcli_fax(mainBranch);
      sym = mainBranch;
    }else{
      sym = "trunk";
    }
  }
  rc = fsl_sym_to_rid(f, sym, FSL_SATYPE_CHECKIN, &rid);
  if(rc) goto end;
  if(prevId == rid){
    f_out("Same version - nothing to do.\n");
    goto end;
  }
  ex.extracted = 0;
  ex.kept = 0;
  ex.quiet = q;
  fsl_repo_checkout_opt cOpt = fsl_repo_checkout_opt_empty;
  cOpt.dryRun = fcli.fDryRun;
  cOpt.vid = rid;
  cOpt.callback = fsl_repo_checkout_f_my;
  cOpt.callbackState = &ex;
  cOpt.fileOverwritePolicy = keep ? FSL_OVERWRITE_NEVER : FSL_OVERWRITE_ALWAYS;
  cOpt.rmMissingFiles = doRm;
  rc = fsl_repo_checkout(f, &cOpt);
  if(rc) goto end;
  f_out("\nChecked out %d file(s) from %s [RID: %"FSL_ID_T_PFMT"].\n",
        ex.extracted, sym, rid);
  if(ex.kept){
    f_out("%d file(s) left unchanged on disk\n", ex.kept);
  }
  if(cOpt.dryRun){
    f_out("Dry-run mode. Rolling back transaction.\n");
    rc = fsl_cx_transaction_end(f, true);
  }
  end:
  if(fsl_cx_transaction_level(f)){
    int const rc2 = fsl_cx_transaction_end(f, !!rc);
    rc = rc ? rc : fsl_cx_uplift_db_error2(f, db, rc2);
  }
  return fcli_end_of_main(rc);
}
