/* -*- 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 template application for libfossil fcli client apps, with
   commentary explaining how to do various common things with the
   API. Copy/paste this and modify it to suit.
*/
#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_ {
  fsl_annotate_opt opt;
} App = {
fsl_annotate_opt_empty_m
};

#if 0
static int fsl_annotate_stepper(void * state,
                                fsl_annotate_opt const * const * opt,
                                fsl_annotate_step const * const * step){
  static const int szHash = 10;
  fsl_outputer const * fout = (fsl_outputer*)state;
  int rc = 0;
  char ymd[24];
  if(step->mtime>0){
    fsl_julian_to_iso8601(step->mtime, &ymd[0], false);
    ymd[10] = 0;
  }
  switch(step->stepType){
    case FSL_ANNOTATE_STEP_VERSION:
      rc = fsl_appendf(fout->out, fout->state,
                       "version %3d: %s %.*s file %.*s\n",
                       step->stepNumber+1, ymd, szHash,
                       step->versionHash, szHash, step->fileHash);
      break;
    case FSL_ANNOTATE_STEP_FULL:
      if(opt->praise){
        rc = fsl_appendf(fout->out, fout->state,
                         "%.*s %s %13.13s: %.*s\n",
                         szHash,
                         opt->fileVersions ? step->fileHash : step->versionHash,
                         ymd, step->username,
                         (int)step->lineLength, step->line);
      }else{
        rc = fsl_appendf(fout->out, fout->state,
                         "%.*s %s %5d: %.*s\n",
                         szHash, opt->fileVersions ? step->fileHash : step->versionHash,
                         ymd, step->lineNumber,
                         (int)step->lineLength, step->line);
      }
      break;
    case FSL_ANNOTATE_STEP_LIMITED:
      if(opt->praise){
        rc = fsl_appendf(fout->out, fout->state,
                         "%*s %.*s\n", szHash+26, "",
                         (int)step->lineLength, step->line);
      }else{
        rc = fsl_appendf(fout->out, fout->state,
                         "%*s %5" PRIu32 ": %.*s\n",
                         szHash+11, "", step->lineNumber,
                         (int)step->lineLength, step->line);
      }
      break;
  }
  return rc;
}
#endif

int main(int argc, const char * const * argv ){
  bool ignoreAllSpace = false;
  bool ignoreEOLSpace = false;
  char const * zRevision = 0;
  char const * zOrigin = 0;
  fsl_buffer fnamebuf = fsl_buffer_empty;

  /**
     Set up flag handling, which is used for processing
     basic CLI flags and generating --help text output.
  */
  const fcli_cliflag FCliFlags[] = {
    // FCLI_FLAG_xxx macros are convenience forms for initializing
    // these members...
    FCLI_FLAG_BOOL("p","praise",&App.opt.praise,
                   "Use praise/blame mode."),
    FCLI_FLAG_BOOL("f","file-versions",&App.opt.fileVersions,
                   "List file blob versions instead of checkin versions."),
    FCLI_FLAG_BOOL("w","ignore-all-space",&ignoreAllSpace,
                   "Ignore all whitespace changes."),
    FCLI_FLAG_BOOL("Z","ignore-trailing-space",&ignoreEOLSpace,
                   "Ignore end-of-line whitespace."),
    FCLI_FLAG("v","version", "string",&zRevision,
              "Checkin containing the input file (default=checkout)."),
    FCLI_FLAG("o","origin", "string", &zOrigin,
              "Origin checkin version (default root of "
              "the tree)."),
    FCLI_FLAG("f","file", "filename",&App.opt.filename,
              "Repo-relative file to annote. May be provided as the "
              "first non-flag argument."),
    FCLI_FLAG_I32("n","limit", "int>=0",(int32_t*)&App.opt.limitVersions,
                  "Limit the number of historical versions to check. "
                  "0=no limit."),
    FCLI_FLAG_I32(NULL,"ms", "int>=0",(int32_t*)&App.opt.limitMs,
                  "Limit annotation to approx. this number of "
                  "milliseconds of work. 0=no limit."),
    FCLI_FLAG_BOOL(NULL,"versions", &App.opt.dumpVersions,
                   "Prefix output with a list of all analyzed versions."),
    fcli_cliflag_empty_m // list MUST end with this (or equivalent)
  };
  const fcli_help_info FCliHelp = {
    "Outputs an annoted listing of a file's contents.",
    "[options] FILENAME",
    NULL // optional callback which outputs app-specific help
  };
  fcli.cliFlags = FCliFlags;
  fcli.appHelp = &FCliHelp;

  int rc = fcli_setup(argc, argv);
  if(rc) goto end;

  fsl_cx * const f = fcli_cx();

  if(!App.opt.filename){
    App.opt.filename = fcli_next_arg(true);
    if(!App.opt.filename){
      rc = fcli_err_set(FSL_RC_MISUSE,
                        "Missing required filename argument. Try --help.");
      goto end;
    }
  }
  if(fsl_cx_db_ckout(f)
     && 0==fsl_stat(App.opt.filename, NULL, false)){
    // We're in a checkout and the file exists. Canonicalize
    // the filename relative to the repo root...
    rc = fsl_ckout_filename_check(f, true, App.opt.filename,
                                  &fnamebuf);
    if(rc) goto end;
    App.opt.filename = fsl_buffer_cstr(&fnamebuf);
  }

  if(zRevision){
    rc = fsl_sym_to_rid(f, zRevision, FSL_SATYPE_CHECKIN,
                        &App.opt.versionRid);
    if(rc) goto end;
  }else{
    fsl_ckout_version_info(f, &App.opt.versionRid, &zRevision);
    if(!zRevision){
      rc = fcli_err_set(FSL_RC_MISUSE,
                        "Cannot determine --revision value.");
      goto end;
    }
  }
  if(zOrigin){
    rc = fsl_sym_to_rid(f, zOrigin, FSL_SATYPE_CHECKIN,
                        &App.opt.originRid);
    if(rc) goto end;
  }
  if((rc=fcli_has_unused_args(false))) goto end;
  if(ignoreAllSpace) App.opt.spacePolicy = 1;
  else if(ignoreEOLSpace) App.opt.spacePolicy = -1;
  fsl_outputer myOut = fsl_outputer_FILE;
  myOut.state = stdout;
  App.opt.out = fsl_annotate_step_f_fossilesque;
  App.opt.outState = &myOut;
  rc = fsl_annotate(f, &App.opt);

  end:
  fsl_buffer_clear(&fnamebuf);
  return fcli_end_of_main(rc)
    /* Will report any pending error state and return either
       EXIT_SUCCESS or EXIT_FAILURE. */;
}
