/* -*- 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).
*/
/*************************************************************************
  This file implements ticket-related parts of the library.
*/
#include "fossil-scm/fossil-internal.h"
#include <assert.h>
#include <string.h> /* memcmp() */

int fsl__cx_ticket_create_table(fsl_cx * const f){
  fsl_db * const db = fsl_needs_repo(f);
  int rc;
  if(!db) return FSL_RC_NOT_A_REPO;
  rc = fsl_cx_exec_multi(f,
                         "DROP TABLE IF EXISTS ticket;"
                         "DROP TABLE IF EXISTS ticketchng;"
                         );
  if(!rc){
    fsl_buffer * const buf = &f->cache.fileContent;
    fsl_buffer_reuse(buf);
    rc = fsl_cx_schema_ticket(f, buf);
    if(!rc) rc = fsl_cx_exec_multi(f, "%b", buf);
  }
  return rc;
}

static int fsl_tkt_field_id(fsl_list const * jli, const char *zFieldName){
  int i;
  fsl_card_J const * jc;
  for(i=0; i<(int)jli->used; ++i){
    jc = (fsl_card_J const *)jli->list[i];
    if( !fsl_strcmp(zFieldName, jc->field) ) return i;
  }
  return -1;
}

int fsl__cx_ticket_load_fields(fsl_cx * const f, bool forceReload){
  fsl_stmt q = fsl_stmt_empty;
  int i, rc = 0;
  fsl_list * const li = &f->ticket.customFields;
  fsl_card_J * jc;
  if( !fsl_needs_repo(f) ){
    return FSL_RC_NOT_A_REPO;
  }else if(li->used){
    if(!forceReload) return 0;
    fsl__card_J_list_free(li, false);
    /* Fall through and reload ... */
  }
  rc = fsl_cx_prepare(f, &q, "PRAGMA table_info(ticket)");
  if(!rc) while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){
    char const * zFieldName = fsl_stmt_g_text(&q, 1, NULL);
    f->ticket.hasTicket = 1;
    if( 0==memcmp(zFieldName,"tkt_", 4)){
      if( 0==fsl_strcmp(zFieldName,"tkt_ctime")) f->ticket.hasCTime = 1;
      continue;
    }
    jc = fsl_card_J_malloc(0, zFieldName, NULL);
    if(!jc){
      rc = FSL_RC_OOM;
      break;
    }
    jc->flags = FSL_CARD_J_TICKET;
    rc = fsl_list_append(li, jc);
    if(rc){
      fsl_card_J_free(jc);
      break;
    }
  }
  fsl_stmt_finalize(&q);
  if(rc) goto end;

  rc = fsl_cx_prepare(f, &q, "PRAGMA table_info(ticketchng)");
  if(!rc) while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){
    char const * zFieldName = fsl_stmt_g_text(&q, 1, NULL);
    f->ticket.hasChng = 1;
    if( 0==memcmp(zFieldName,"tkt_", 4)){
      if( 0==fsl_strcmp(zFieldName,"tkt_rid")) f->ticket.hasChngRid = 1;
      continue;
    }
    if( (i=fsl_tkt_field_id(li, zFieldName)) >= 0){
      jc = (fsl_card_J*)li->list[i];
      jc->flags |= FSL_CARD_J_CHNG;
      continue;
    }
    jc = fsl_card_J_malloc(0, zFieldName, NULL);
    if(!jc){
      rc = FSL_RC_OOM;
      break;
    }
    jc->flags = FSL_CARD_J_CHNG;
    rc = fsl_list_append(li, jc);
    if(rc){
      fsl_card_J_free(jc);
      break;
    }
  }
  fsl_stmt_finalize(&q);
  end:
  if(!rc){
    fsl_list_sort(li, fsl__qsort_cmp_J_cards);
  }
  return rc;
}

int fsl__ticket_rebuild(fsl_cx * const f, char const * zTktKCard){
  int rc;
  fsl_id_t tktRid;
  fsl_id_t tagId;
  fsl_db * const db = fsl_needs_repo(f);
  fsl_stmt q = fsl_stmt_empty;
  if(!db) return FSL_RC_NOT_A_REPO;
  assert(!f->cache.isCrosslinking);
  rc = fsl__cx_ticket_load_fields(f, false);
  if(rc) goto end;
  else if(!f->ticket.hasTicket) return 0;
  if(f->flags & FSL_CX_F_SKIP_UNKNOWN_CROSSLINKS){
    return 0;
  }else{
    char * const zTag = fsl_mprintf("tkt-%s", zTktKCard);
    if(!zTag){
      rc = FSL_RC_OOM;
      goto end;
    }
    tagId = fsl_tag_id(f, zTag, true);
    fsl_free(zTag);
  }
  if(tagId<0){
    rc = f->error.code;
    assert(0!=rc);
    goto end;
  }
  tktRid = fsl_db_g_id(db, 0, "SELECT tkt_id FROM ticket "
                       "WHERE tkt_uuid=%Q", zTktKCard);
  if(tktRid>0){
    if(f->ticket.hasChng){
      rc = fsl_cx_exec(f, "DELETE FROM ticketchng "
                       "WHERE tkt_id=%" FSL_ID_T_PFMT,
                       tktRid);
    }
    if(!rc) rc = fsl_cx_exec(f, "DELETE FROM ticket "
                             "WHERE tkt_id=%" FSL_ID_T_PFMT,
                             tktRid);
    if(rc) goto end;
  }
  tktRid = 0;
  rc = fsl_cx_prepare(f, &q, "SELECT rid FROM tagxref "
                      "WHERE tagid=%" FSL_ID_T_PFMT
                      " ORDER BY mtime", tagId);
  while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&q)){
    fsl_deck deck = fsl_deck_empty;
    fsl_id_t const rid = fsl_stmt_g_id(&q, 0);
    rc = fsl_deck_load_rid(f, &deck, rid, FSL_SATYPE_TICKET);
    if(0==rc){
#if 0
      /* TODOs... */
      assert(deck.rid==rid);
      rc = fsl__ticket_insert(f, &deck, rid, tktRid, &tgtRid)
        /* See fossil(1) tkt.c:ticket_insert() */;
      if(rc) goto outro;
      rc = fsl__deck_ticket_event(&deck, createFlag, tagId)
        /* See fossil(1) manifest.c:mainfest_ticket_event() */;
#else
      rc = fsl_cx_err_set(f, FSL_RC_NYI,
                          "MISSING: a huge block of TICKET stuff from/via "
                          "manifest_crosslink(). It requires infrastructure "
                          "libfossil does not yet have.");
#endif
    }
    //outro:
    fsl_deck_finalize(&deck);
  }
  end:
  fsl_stmt_finalize(&q);
  return rc;
}

