/* -*- 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/
  
  *****************************************************************************
   This file houses some of the "leaf"-related APIs.
*/
#include <assert.h>

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

/* Only for debugging */
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)

int fsl_repo_leaves_rebuild(fsl_cx * f){
  fsl_db * db = f ? fsl_cx_db_repo(f) : NULL;
  return !db ? FSL_RC_MISUSE : fsl_db_exec_multi(db,
    "DELETE FROM leaf;"
    "INSERT OR IGNORE INTO leaf"
    "  SELECT cid FROM plink"
    "  EXCEPT"
    "  SELECT pid FROM plink"
    "   WHERE coalesce((SELECT value FROM tagxref"
                       " WHERE tagid=%d AND rid=plink.pid),'trunk')"
         " == coalesce((SELECT value FROM tagxref"
                       " WHERE tagid=%d AND rid=plink.cid),'trunk')",
    FSL_TAGID_BRANCH, FSL_TAGID_BRANCH
  );
}

fsl_int_t fsl_count_nonbranch_children(fsl_cx * f, fsl_id_t rid){
  fsl_int32_t rv = 0;
  int rc;
  fsl_db * db = f ? fsl_cx_db_repo(f) : NULL;
  if(!db || !db->dbh || (rid<=0)) return -1;
  rc = fsl_db_get_int32(db, &rv,
                        "SELECT count(*) FROM plink "
                        "WHERE pid=%"FSL_ID_T_PFMT" "
                        "AND isprim "
                        "AND coalesce((SELECT value FROM tagxref "
                        "WHERE tagid=%d AND rid=plink.pid), 'trunk')"
                        "=coalesce((SELECT value FROM tagxref "
                        "WHERE tagid=%d AND rid=plink.cid), 'trunk')",
                        rid, FSL_TAGID_BRANCH, FSL_TAGID_BRANCH);
  return rc ? -2 : rv;
}

/*
   Return >0 if the check-in with RID=rid is a leaf, 0
   if it is not, negative value on error.
  
   A leaf has no children in the same branch. 
*/
char fsl_rid_is_leaf(fsl_cx * f, fsl_id_t rid){
  fsl_int32_t rv = -1;
  int rc;
  fsl_db * db = f ? fsl_cx_db_repo(f) : NULL;
  fsl_stmt * st = NULL;
  if(!db || !db->dbh || (rid<=0)) return 0;
  rc = fsl_db_prepare_cached(db, &st,
       "SELECT 1 FROM plink "
       "WHERE pid=? "
       "AND coalesce((SELECT value FROM tagxref "
                     "WHERE tagid=%d "
                     "AND rid=plink.pid), 'trunk')"
           "=coalesce((SELECT value FROM tagxref "
                       "WHERE tagid=%d "
                       "AND rid=plink.cid), 'trunk')",
       FSL_TAGID_BRANCH, FSL_TAGID_BRANCH);
  if(!rc){
    rc = fsl_stmt_bind_id(st, 1, rid);
    if(!rc){
      rc = fsl_stmt_step(st);
      if(FSL_RC_STEP_ROW==rc){
        rv = 1;
      }else if(FSL_RC_STEP_DONE==rc) rc = 0;
    }
    fsl_stmt_cached_yield(st);
  }
  return rc ? 0 : (rv==-1);
}

int fsl_repo_leaf_check(fsl_cx * f, fsl_id_t rid){
  fsl_db * db = f ? fsl_cx_db_repo(f) : NULL;
  if(!db || !db->dbh) return FSL_RC_MISUSE;
  else if(rid<=0) return FSL_RC_RANGE;
  else {
    char isLeaf;
    int rc = 0;
    fsl_stmt * st = NULL;
    isLeaf = fsl_rid_is_leaf(f, rid);
    if( isLeaf ){
      rc = fsl_db_prepare_cached(db, &st,
                                 "DELETE FROM leaf WHERE rid=?");
    }else{
      rc = fsl_db_prepare_cached(db, &st,
                                 "INSERT OR IGNORE INTO leaf VALUES"
                                 "(?)");
    }
    if(!rc && st){
      rc = fsl_stmt_bind_id(st, 1, rid);
      if(!rc) rc = fsl_stmt_step(st);
      fsl_stmt_cached_yield(st);
      if(FSL_RC_STEP_DONE==rc) rc = 0;
    }
    return rc;
  }
}

int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid){
  fsl_db * db = f ? fsl_cx_db_repo(f) : NULL;
  if(!f) return FSL_RC_MISUSE;
  else if(rid<=0) return FSL_RC_RANGE;
  else if(!db) return FSL_RC_NOT_A_REPO;
  else {
    fsl_stmt * parentsOf = NULL;
    int rc = fsl_db_prepare_cached(db, &parentsOf,
                            "SELECT pid FROM plink WHERE "
                            "cid=? AND pid>0");
    if(rc) return rc;
    rc = fsl_stmt_bind_id(parentsOf, 1, rid);
    if(!rc){
      rc = fsl_id_bag_insert(&f->cache.leafCheck, rid);
      while( !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(parentsOf)) ){
        rc = fsl_id_bag_insert(&f->cache.leafCheck,
                               fsl_stmt_g_id(parentsOf, 0));
      }
    }
    fsl_stmt_cached_yield(parentsOf);
    return rc;
  }
}


int fsl_repo_leaf_do_pending_checks(fsl_cx *f){
  fsl_id_t rid;
  int rc = 0;
  for(rid=fsl_id_bag_first(&f->cache.leafCheck);
      !rc && rid; rid=fsl_id_bag_next(&f->cache.leafCheck,rid)){
    rc = fsl_repo_leaf_check(f, rid);
  }
  fsl_id_bag_clear(&f->cache.leafCheck);
  return rc;
}
  
#undef MARKER
