/* -*- 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_ {
  char const * file1;
  char const * file2;
  char const * builderName;
  uint32_t diffFlags;
  int contextLines;
  bool ansiColor;
} App = {
NULL,//file1
NULL,//file2
"unified",//builderName
0/*diffFlags*/,
-1/*contextLines*/,
false/*ansiColor*/
};

static int fdb__create(fsl_dibu **b){
  fsl_dibu_e t = FSL_DIBU_DEBUG;
  switch(*App.builderName){
    case (int)'d':
      break;
    case (int)'j': t = FSL_DIBU_JSON1;
      break;
    case (int)'t': t = FSL_DIBU_TCL;
      break;
    case (int)'u': t = FSL_DIBU_UNIFIED_TEXT;
      break;
    case (int)'s': t = FSL_DIBU_SPLIT_TEXT;
      break;
    default:
      return fcli_err_set(FSL_RC_MISUSE,
                          "Unknown diff builder name: %s",
                          App.builderName);
      break;
  }
  return fsl_dibu_factory(t, b);
}

static int app_stuff(){
  fsl_buffer f1 = fsl_buffer_empty;
  fsl_buffer f2 = fsl_buffer_empty;
  fsl_buffer hash1 = fsl_buffer_empty;
  fsl_buffer hash2 = fsl_buffer_empty;
  fsl_dibu * b = 0;
  fsl_diff_opt opt = fsl_diff_opt_empty;
  int rc = 0;
  int * rawDiff = 0;
#define RC if(rc) goto end
  if('r'!=App.builderName[0]/*raw diff*/){
    rc = fdb__create(&b);
    if(rc) goto end;
    assert(b);
    b->opt = &opt;
  }
  if(App.ansiColor && fsl_isatty(1)){
    fcli_diff_colors(&opt, FCLI_DIFF_COLORS_DEFAULT);
  }
  opt.diffFlags = App.diffFlags;
  opt.contextLines = App.contextLines;
  assert(0==rc);
  opt.out = fsl_output_f_FILE;
  opt.outState = stdout;
  RC;
  rc = fsl_buffer_fill_from_filename(&f1, App.file1);
  RC;
  rc = fsl_buffer_fill_from_filename(&f2, App.file2);
  RC;
  if(1){
    fsl_sha3sum_buffer(&f1, &hash1);
    fsl_sha3sum_buffer(&f2, &hash2);
    opt.hashLHS = fsl_buffer_cstr(&hash1);
    opt.hashRHS = fsl_buffer_cstr(&hash2);
    opt.nameLHS = App.file1;
    opt.nameRHS = App.file2;
  }
  rc = b
    ? fsl_diff_v2(&f1, &f2, b)
    : fsl_diff_v2_raw(&f1, &f2, &opt, &rawDiff);
  RC;
  if(rawDiff){
    f_out("Raw diff triples:\n");
    for(int i = 0; rawDiff[i] || rawDiff[i+1] || rawDiff[i+2]; i+=3){
      f_out(" copy %6d  delete %6d  insert %6d\n",
            rawDiff[i], rawDiff[i+1], rawDiff[i+2]);
    }
  }
  end:
  fsl_free(rawDiff);
  if(b) b->finalize(b);
  fsl_buffer_clear(&f1);
  fsl_buffer_clear(&f2);
  fsl_buffer_clear(&hash1);
  fsl_buffer_clear(&hash2);
  return rc;
#undef RC
}

static int fcli_flag_callback_f_diffflags(fcli_cliflag const *f){
  switch(*f->flagShort){
    case (int)'#': App.diffFlags |= FSL_DIFF2_LINE_NUMBERS;
      break;
    case (int)'v': App.diffFlags |= FSL_DIFF2_INVERT;
      break;
    default: break;
  }
  return 0;
}

int main(int argc, const char * const * argv ){
  bool bogus;
  const fcli_cliflag FCliFlags[] = {
    FCLI_FLAG("b","builer","string",&App.builderName,
              "Builder name. One of: "
              "u (unified), s (split), r (raw), "
              "d (debug), j (json), t (tcl)"),
    FCLI_FLAG_I32("F","diff-flags", "integer", &App.diffFlags,
                  "Bitmask of flags for builder's clientFlags."),
    FCLI_FLAG_I32("c","context", "integer", &App.contextLines,
                  "Number of context lines for the diff."),
    FCLI_FLAG_BOOL_X("#", "line-numbers", &bogus,
                     fcli_flag_callback_f_diffflags,
                     "Enable line numbers for diff formats "
                     "which optionally support it."),
    FCLI_FLAG_BOOL_X("v", "invert", &bogus,
                     fcli_flag_callback_f_diffflags,
                     "Invert the diff's LHS and RHS."),
    FCLI_FLAG_BOOL("a","ansi", &App.ansiColor,
                   "Enable ANSI color if possible."),
    fcli_cliflag_empty_m
  };
  const fcli_help_info FCliHelp = {
    "Test app for fsl_dibu.",
    "file1 file2",
    NULL
  };
  fcli.clientFlags.checkoutDir = NULL; // same effect as global -C flag.

  int rc = fcli_setup_v2(argc, argv, FCliFlags, &FCliHelp);
  if(rc) goto end;

  App.file1 = fcli_next_arg(true);
  App.file2 = fcli_next_arg(true);
  if(!App.file2){
    rc =fcli_err_set(FSL_RC_MISUSE,
                     "Expecting two filenames.");
    goto end;
  }
  if((rc=fcli_has_unused_args(false))) goto end;
  
  if(0>App.contextLines) App.contextLines = fsl_diff_opt_empty.contextLines;
  else if(0==App.contextLines) App.diffFlags |= FSL_DIFF2_CONTEXT_ZERO;
  rc = app_stuff();

  end:
  return fcli_end_of_main(rc);
}
