/* -*- 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

  Heavily indebted to the Fossil SCM project (https://fossil-scm.org).  

  *****************************************************************************
  A test/demo app for working with the libfossil config db API.
*/

#include "fossil-scm/fossil-cli.h"
#include "fossil-scm/fossil-internal.h"


static struct ConfigApp {
  bool doGlobal;
  bool doLocal;
  bool doRepo;
  bool showValues;
  const char * lsGlob;
  fsl_confdb_e mode0;
} ConfigApp = {
0,0,0,
0/*showValues*/,
NULL/*lsGlob*/,
FSL_CONFDB_GLOBAL/*mode0*/
};

static fcli_cliflag cliFlagsGlobal[] = {
  FCLI_FLAG_BOOL("g", "global", &ConfigApp.doGlobal,
                 "Use the global config db."),
  FCLI_FLAG_BOOL("c", "checkout", &ConfigApp.doLocal,
                 "Use the checkout-local config."),
  FCLI_FLAG_BOOL("r", "repository", &ConfigApp.doRepo,
                 "Use the repository-local config."),
  fcli_cliflag_empty_m
};

static fcli_cliflag cliFlagsLs[] = {
  FCLI_FLAG(0, "glob", "string", &ConfigApp.lsGlob,
            "Only list properties matching this glob."),
  FCLI_FLAG_BOOL("v", "values", &ConfigApp.showValues,
                 "List property values."),
  fcli_cliflag_empty_m
};

static fcli_cliflag cliFlagsSet[] = {
  fcli_cliflag_empty_m
};

static fcli_cliflag cliFlagsUnset[] = {
  // TODO?: --glob to unset all matching a glob.
  fcli_cliflag_empty_m
};

static int fapp_ls(fsl_cx * f, fsl_confdb_e mode,
                   char const * nameGlob, char showVals){
  int rc;
  fsl_db * db = fsl_config_for_role(f, mode);
  fsl_stmt st = fsl_stmt_empty;
  int nameWidth = -30;
  if(!db){
    if(FSL_CONFDB_GLOBAL==mode){ /* special case: auto-open config db */
      rc = fsl_config_open(f, NULL);
      if(!rc){
        db = fsl_cx_db_config(f);
        assert(db);
      }
    }
    if(!db){
      return fcli_err_set(FSL_RC_MISUSE,
                          "Config db role #%d is not opened.",
                          mode);
    }
  }
  rc = fsl_db_prepare(db, &st, "SELECT name, value FROM %s ORDER BY name",
                      fsl_config_table_for_role(mode));
  while(FSL_RC_STEP_ROW==fsl_stmt_step(&st)){
    char const * name = fsl_stmt_g_text(&st, 0, NULL);
    if(nameGlob && !fsl_str_glob(nameGlob, name)) continue;
    if(showVals){
      f_out("%*s %s\n",
            nameWidth, name,
            fsl_stmt_g_text(&st,1,NULL));
    }else{
      f_out("%s\n", name);
    }
  }
  fsl_stmt_finalize(&st);
  return rc;
}

static int fcmd_ls(fcli_command const *fc){
  fsl_cx * f = fcli_cx();
  int didSomething = 0;
  int rc = fcli_process_flags(fc->flags);
  if(rc || (rc = fcli_has_unused_flags(0))){
    return rc;
  }


  /* f_out("Global config db: %s\n", fsl_cx_db_file_config(f, NULL)); */
#define DUMP(MODE) \
  ++didSomething; \
  rc = fapp_ls(f, MODE, ConfigApp.lsGlob, ConfigApp.showValues)

  if(ConfigApp.doGlobal){
    DUMP(FSL_CONFDB_GLOBAL);
  }
  if(!rc && ConfigApp.doRepo){
    DUMP(FSL_CONFDB_REPO);
  }
  if(!rc && ConfigApp.doLocal){
    DUMP(FSL_CONFDB_CKOUT);
  }
#undef DUMP
  assert(didSomething);
  return rc;
}


static int fcmd_set(fcli_command const *fc){
  const char * key = NULL;
  const char * val = NULL;
  fsl_cx * f = fcli_cx();
  int rc = fcli_process_flags(fc->flags);
  if(rc || (rc = fcli_has_unused_flags(0))){
    return rc;
  }
  key = fcli_next_arg(1);
  if(!key){
    return fcli_err_set(FSL_RC_MISUSE,"Missing property key argument.");
  }
  val = fcli_next_arg(1);
  if(val){
    FCLI_V(("Setting %s = %s\n", key, val));
    rc = fsl_config_set_text(f, ConfigApp.mode0, key, val);
  }else{
    char * v = fsl_config_get_text(f, ConfigApp.mode0, key, 0);
    if(v){
      f_out("%s\n", v);
      fsl_free(v);
    }else{
      rc = fcli_err_set(FSL_RC_NOT_FOUND,
                        "Config key not found: %s", key);
    }
  }
  return rc;
}

static int fcmd_unset(fcli_command const *fc){
  const char * key = NULL;
  fsl_cx * f = fcli_cx();
  unsigned int count = 0;
  int rc = fcli_process_flags(fc->flags);
  if(rc || (rc = fcli_has_unused_flags(0))){
    return rc;
  }
  rc = fsl_config_transaction_begin(f, ConfigApp.mode0);
  if(!rc){
    while( !rc && (key = fcli_next_arg(1)) ){
      ++count;
      FCLI_V(("Unsetting: %s\n", key));
      rc = fsl_config_unset(f, ConfigApp.mode0, key);
    }
    fsl_config_transaction_end(f, ConfigApp.mode0, rc ? 1 : 0);
  }
  if(!count){
    rc = fcli_err_set(FSL_RC_MISUSE,"Missing property key argument(s).");
  }
  return rc;
}


static const fcli_command ConfigCmds[] = {
{"ls", "List config properties.",
 fcmd_ls, cliFlagsLs},
{"set", "Get/set individual config properties.",
 fcmd_set, cliFlagsSet},
{"unset", "Unset config properties. "
    "Pass it all property keys to unset.",
 fcmd_unset, cliFlagsUnset},
{NULL,NULL, NULL}
};

static void fcli_local_help(){
  puts("Config commands and their options:\n");
  fcli_command_help(ConfigCmds, false);
}

static int fapp_main(fsl_cx * f){
  /*
    Set the main config mode for commands which use only
    one config. Priority order: -c -r -g
  */
  if(ConfigApp.doGlobal){
    int rc;
    ConfigApp.mode0 = FSL_CONFDB_GLOBAL;
    rc = fsl_config_open( f, 0 );
    if(rc) return rc;
  }
  else if(ConfigApp.doLocal) ConfigApp.mode0 = FSL_CONFDB_CKOUT;
  else if(ConfigApp.doRepo) ConfigApp.mode0 = FSL_CONFDB_REPO;
  else{
    return fcli_err_set(FSL_RC_MISUSE,
                        "No config db specified. Use one of: "
                        "-g[lobal], -r[epo], -c[heckout]");
  }
  return fcli_dispatch_commands(ConfigCmds, 0);
}

int main(int argc, char const * const * argv ){
  int rc = 0;
  fcli.cliFlags = cliFlagsGlobal;
  fcli_help_info FCliHelp = {
    "List and manipulate fossil configuration vars.",
    "ls|set|unset [options]",
    fcli_local_help
  };
  fcli.appHelp = &FCliHelp;
  rc = fcli_setup(argc, argv);
  if(!rc){
    rc = fapp_main(fcli_cx());
  }
  return fcli_end_of_main(rc);
}
