/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
   Copyright (c) 2013 D. Richard Hipp
  
   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.
  
   Author contact information:
     drh@hwaci.com
     http://www.hwaci.com/drh/
  
  *****************************************************************************
   A simple tool for dumping blobs to stdout.
*/

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

static void fcli_local_help(){
  printf("Usage:\n\t%s [options]\n\n", fcli.appName);

  puts("Outputs status info for the current checkout. Options:\n");
  puts("\n\t-f|--no-files disables the status check for local files.\n");
  puts("\n\t--utc enables UTC timestamps (default is local time).\n");
  puts("\n\t-v|--version specifies the checkin version to show "
       "(default is checkout version).\n");
  puts("\n\t--no-reset is an internal debugging flag which tells this app "
       "not to re-set the vfile contents to be those of the current checkout. "
       "For compatibility with fossil(1), do not use it. It is only for testing.\n");
}

static char const * fsl_ckout_change_label(fsl_checkout_change_t change){
  switch(change){
    case FSL_CKOUT_CHANGE_NONE: return NULL;
    case FSL_CKOUT_CHANGE_MOD: return "MODIFIED";
    case FSL_CKOUT_CHANGE_MERGE_MOD: return "MERGE-MOD";
    case FSL_CKOUT_CHANGE_MERGE_ADD: return "MERGE-ADD";
    case FSL_CKOUT_CHANGE_INTEGRATE_MOD: return "INTEGRATE-MOD";
    case FSL_CKOUT_CHANGE_INTEGRATE_ADD: return "INTEGRATE-ADD";
    case FSL_CKOUT_CHANGE_ADDED: return "ADDED";
    case FSL_CKOUT_CHANGE_REMOVED: return "REMOVED";
    case FSL_CKOUT_CHANGE_MISSING: return "MISSING";
    case FSL_CKOUT_CHANGE_RENAMED: return "RENAMED";
    case FSL_CKOUT_CHANGE_CONFLICT: return "CONFLICT";
    case FSL_CKOUT_CHANGE_NOT_A_FILE: return "NOT-A-FILE";
    case FSL_CKOUT_CHANGE_CHERRYPICK: return "CHERRYPICK";
    case FSL_CKOUT_CHANGE_BACKOUT: return "BACKOUT";
    default:
      return "?!NO WAY!?";
  }
}

/**
    A fsl_checkout_changes_f() impl which outputs change status to f_out().
 */
static int fsl_checkout_changes_fapp(void * state, fsl_checkout_change_t change,
                                     char const * filename, char const * origName){
  if(1==++*((int*)state)){
    f_out("\nLocal changes compared to this version:\n\n");
  }
  f_out("%-15s", fsl_ckout_change_label(change));
  if(FSL_CKOUT_CHANGE_RENAMED==change){
    assert(origName && *origName);
    f_out("%s\n%14s %s\n", origName, "to", filename);
  }else{
    f_out("%s\n", filename);
  }
  return 0;
}


static int fapp_show_local_changes(fsl_cx * f, fsl_id_t vid, char doScan){
  int rc = 0;
  if(doScan) rc = fsl_checkout_changes_scan(f);
  if(!rc){
    int counter = 0;
    rc = fsl_checkout_changes_visit(f, vid, 0,
                                    fsl_checkout_changes_fapp, &counter);
    if(!rc){
      f_out("%s\n", counter ? "" : "\nNo local changes.");
    }
  }
  return rc;
}

/**
    I
 */
static int fapp_show_info(fsl_cx * f, fsl_id_t rid, char useUtc){
  int rc = 0;
  fsl_stmt st = fsl_stmt_empty;
  fsl_db * dbR = fsl_cx_db_repo(f);
  fsl_db * dbC = fsl_cx_db_checkout(f);
  int lblWidth = -20;
  assert(dbR);
  assert(dbC);

  {
    fsl_id_t ckoutRid = 0;
    fsl_uuid_cstr ckoutUuid = NULL;
    fsl_checkout_version_info(f, &ckoutRid, &ckoutUuid);
    assert(ckoutUuid && (ckoutRid>0));
  }

  f_out("%*s %s\n", lblWidth, "repository-db:",
        fsl_cx_db_file_repo(f, NULL));
  f_out("%*s %s\n", lblWidth, "checkout-root:",
        fsl_cx_dir_name_checkout(f, NULL));

  rc = fsl_db_prepare(dbR, &st, "SELECT "
                      /*0*/"datetime(event.mtime%s) AS timestampString, "
                      /*1*/"coalesce(euser, user) AS user, "
                      /*2*/"(SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref "
                      "WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid "
                      "AND tagxref.rid=blob.rid AND tagxref.tagtype>0) as tags, "
                      /*3*/"coalesce(ecomment, comment) AS comment, "
                      /*4*/"uuid AS uuid "
                      "FROM event JOIN blob "
                      "WHERE "
                      "event.type='ci' "
                      "AND blob.rid=%"FSL_ID_T_PFMT" "
                      "AND blob.rid=event.objid "
                      "ORDER BY event.mtime DESC",
                      useUtc ? "" : ", 'localtime'",
                      (fsl_id_t)rid);
  if(rc) goto dberr;
  if( FSL_RC_STEP_ROW != fsl_stmt_step(&st)){
    fcli_err_set(FSL_RC_ERROR, "Event data for checkout not found.");
    goto end;

  }

  f_out("%*s %s %s %s (RID %"FSL_ID_T_PFMT")\n",
        lblWidth, "checkout-version:",
        fsl_stmt_g_text(&st, 4, NULL),
        fsl_stmt_g_text(&st, 0, NULL),
        useUtc ? "UTC" : "local",
        (fsl_id_t)rid );

  {
    /* list parent(s) */
    fsl_stmt stP = fsl_stmt_empty;
    rc = fsl_db_prepare(dbR, &stP, "SELECT "
                        "uuid, pid, isprim "
                        "FROM plink JOIN blob ON pid=rid "
                        "WHERE cid=%"FSL_ID_T_PFMT" "
                        "ORDER BY isprim DESC, mtime DESC /*sort*/",
                        (fsl_id_t)rid);
    if(rc) goto dberr;
    while( FSL_RC_STEP_ROW == fsl_stmt_step(&stP) ){
      char const * zLabel = fsl_stmt_g_int32(&stP,2)
        ? "parent:" : "merged-from:";
      f_out("%*s %s\n", lblWidth, zLabel,
            fsl_stmt_g_text(&stP, 0, NULL));
      
    }
    fsl_stmt_finalize(&stP);
  }
  {
    /* list children */
    fsl_stmt stC = fsl_stmt_empty;
    rc = fsl_db_prepare(dbR, &stC, "SELECT "
                        "uuid, cid, isprim "
                        "FROM plink JOIN blob ON cid=rid "
                        "WHERE pid=%"FSL_ID_T_PFMT" "
                        "ORDER BY isprim DESC, mtime DESC /*sort*/",
                        (fsl_id_t)rid);
    if(rc) goto dberr;
    while( FSL_RC_STEP_ROW == fsl_stmt_step(&stC) ){
      char const * zLabel = fsl_stmt_g_int32(&stC,2)
        ? "child:" : "merged-into:";
      f_out("%*s %s\n", lblWidth, zLabel,
            fsl_stmt_g_text(&stC, 0, NULL));
      
    }
    fsl_stmt_finalize(&stC);
  }

  f_out("%*s %s\n", lblWidth, "user:",
        fsl_stmt_g_text(&st, 1, NULL));

  f_out("%*s %s\n", lblWidth, "tags:",
        fsl_stmt_g_text(&st, 2, NULL));

  f_out("%*s %s\n", lblWidth, "comment:",
        fsl_stmt_g_text(&st, 3, NULL));

  dberr:
  if(rc){
    fsl_cx_uplift_db_error(f, dbR);
  }
  end:
  fsl_stmt_finalize(&st);

  return rc;
}



int main(int argc, char * const * argv ){
  int rc = 0;
  fsl_cx * f;
  fsl_db * db;
  fsl_id_t rid = 0;
  char skipFiles;
  char useUtc;
  char * zVersion = NULL;
  fsl_id_t ckoutRid = 0;
  char resetVfile;
  fcli.appHelp = fcli_local_help;
  rc = fcli_setup(argc, argv);
  if(FSL_RC_BREAK==rc) /* --help */ return 0;
  else if(rc) goto end;
  f = fcli.f;
  db = fsl_cx_db_checkout(f);
  if(!db){
    rc = fcli_err_set( FSL_RC_NOT_A_CHECKOUT,
                       "This app requires a checkout db.");
    goto end;
  }

  skipFiles = fcli_flag2("f","no-files", NULL);
  useUtc = fcli_flag("utc", NULL);
  fcli_flag2("v","version", &zVersion);
  resetVfile = !fcli_flag("no-reset",NULL);

  if(fcli_has_unused_flags(0)) goto end;

  fsl_checkout_version_info(f, &ckoutRid, NULL);

  if(zVersion){
    rc = fsl_sym_to_rid(f, zVersion, FSL_CATYPE_CHECKIN, &rid);
    if(rc) goto end;
  }
  if(!rid){
    rid = ckoutRid;
  }
  if( !fsl_rid_is_a_checkin(f, rid) ) {
    rc = fcli_err_set(rc, "%s does not refer to a checkin version.",
                      zVersion ? zVersion : "RID");
    goto end;
  }

  rc = fapp_show_info(f, rid, useUtc);
  if(!rc && !skipFiles){
    rc = fapp_show_local_changes(f, rid, resetVfile);
  }

  end:
  fsl_free(zVersion);
  return fcli_err_report(1) ? EXIT_FAILURE : EXIT_SUCCESS;
}
