#include "VERSION.h"
#include "config.h"
#include "json_artifact.h"

#if INTERFACE
#include "json_detail.h"
#endif

/*
** Internal callback for /json/artifact handlers. rid refers to
** the rid of a given type of artifact, and each callback is
** specialized to return a JSON form of one type of artifact.
*/
typedef cson_value * (*artifact_f)( int rid );

typedef struct ArtifactDispatchEntry {
  /**
     Artifact type name, e.g. "checkin".
   */
  char const * name;
  /**
     JSON construction callback.
   */
  artifact_f func;
  /**
     Must return true if g.perm has the proper permissions to fetch
     this info, else false. If it returns false, func() is skipped
     (producing no extra payload output).
   */
  char (*permCheck)();
} ArtifactDispatchEntry;


/*
** Generates an artifact Object for the given rid/zUuid. rid
** must refer to a Checkin.
**
** Returned value is NULL or an Object owned by the caller.
*/
cson_value * json_artifact_for_ci( int rid, char showFiles ){
  char const * zParent = NULL;
  cson_value * v = NULL;
  Stmt q;
  static cson_value * eventTypeLabel = NULL;
  if(!eventTypeLabel){
    eventTypeLabel = json_new_string("commit");
    json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel, 1);
  }
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );

  db_prepare(&q, 
             "SELECT uuid, "
             " strftime('%%s',mtime), "
             " user, "
             " comment,"
             " strftime('%%s',omtime)"
             " FROM blob, event"
             " WHERE blob.rid=%d"
             "   AND event.objid=%d",
             rid, rid
             );
  if( db_step(&q)==SQLITE_ROW ){
    cson_object * o;
    cson_value * tmpV = NULL;
    v = cson_value_new_object();
    o = cson_value_get_object(v);
    const char *zUuid = db_column_text(&q, 0);
    char * zTmp;
    const char *zUser;
    const char *zComment;
    char * zEUser, * zEComment;
    int mtime, omtime;
#define SET(K,V) cson_object_set(o,(K), (V))
    SET("artifactType", eventTypeLabel );
    SET("uuid",json_new_string(zUuid));
    SET("isLeaf", cson_value_new_bool(is_a_leaf(rid)));
    zUser = db_column_text(&q,2);
    zEUser = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_USER, rid);
    if(zEUser){
      SET("user", json_new_string(zEUser));
      if(0!=strcmp(zEUser,zUser)){
        SET("originUser",json_new_string(zUser));
      }
      free(zEUser);
    }else{
      SET("user",json_new_string(zUser));
    }

    zComment = db_column_text(&q,3);
    zEComment = db_text(0, 
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);
    if(zEComment){
      SET("comment",json_new_string(zEComment));
      if(0 != strcmp(zEComment,zComment)){
        SET("originComment", json_new_string(zComment));
      }
      free(zEComment);
    }else{
      SET("comment",json_new_string(zComment));
    }

    mtime = db_column_int(&q,1);
    SET("mtime",json_new_int(mtime));
    omtime = db_column_int(&q,4);
    if(omtime && (omtime!=mtime)){
      SET("originTime",json_new_int(omtime));
    }

    if(zParent){
      SET("parentUuid", json_new_string(zParent));
    }

    tmpV = json_tags_for_rid(rid);
    if(tmpV){
      SET("tags",tmpV);
    }

    if( showFiles ){
      cson_value * fileList = json_get_changed_files(rid);
      if(fileList){
        SET("files",fileList);
      }
    }


#undef SET
  }
  db_finalize(&q);
  return v;
}

/*
** Sub-impl of /json/artifact for checkins.
*/
static cson_value * json_artifact_ci( int rid ){
  return json_artifact_for_ci(rid, 1);
}

static char perms_can_read(){
  return g.perm.Read ? 1 : 0;
}

static ArtifactDispatchEntry ArtifactDispatchList[] = {
{"checkin", json_artifact_ci, perms_can_read},
{"tag", NULL, perms_can_read},
{"ticket", NULL, perms_can_read},
{"wiki", NULL, perms_can_read},
{NULL,NULL,NULL}
};

/*
** Impl of /json/artifact
*/
cson_value * json_page_artifact(){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  char const * zName = NULL;
  char const * zType = NULL;
  char const * zUuid = NULL;
  Blob uuid = empty_blob;
  int rc;
  int rid;
  zName = g.isHTTP
    ? json_getenv_cstr("uuid")
    : find_option("uuid","u",1);
  if(!zName||!*zName){
    zName = json_command_arg(g.json.dispatchDepth+1);
    if(!zName || !*zName) {
      g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
      return NULL;
    }
  }

  if( validate16(zName, strlen(zName)) ){
    if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
      zType = "ticket";
      goto end_ok;
    }
    if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){
      zType = "tag";
      goto end_ok;
    }
  }
  blob_set(&uuid,zName);
  rc = name_to_uuid(&uuid,-1,"*");
  if(1==rc){
    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
    goto error;
  }else if(2==rc){
    g.json.resultCode = FSL_JSON_E_AMBIGUOUS_UUID;
    goto error;
  }
  zUuid = blob_str(&uuid);
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%s'", zUuid);
  if(0==rid){
    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
    goto error;
  }
  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid)
      || db_exists("SELECT 1 FROM plink WHERE cid=%d", rid)
      || db_exists("SELECT 1 FROM plink WHERE pid=%d", rid)){
    zType = "checkin";
    goto end_ok;
  }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
                      " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
    zType = "wiki";
    goto end_ok;
  }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
                      " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){
    zType = "ticket";
    goto end_ok;
  }else{
    g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
    goto error;
  }

  error:
  assert( 0 != g.json.resultCode );
  goto veryend;

  end_ok:
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  assert( NULL != zType );
  cson_object_set( pay, "type", json_new_string(zType) );
  /*cson_object_set( pay, "uuid", json_new_string(zUuid) );*/
  cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) );
  cson_object_set( pay, "rid", cson_value_new_integer(rid) );
  ArtifactDispatchEntry const * disp = &ArtifactDispatchList[0];
  for( ; disp->name; ++disp ){
    if(0!=strcmp(disp->name, zType)){
      continue;
    }else{
      cson_value * entry;
      if( ! (*disp->permCheck)() ){
        break;
      }
      entry = (*disp->func)(rid);
      if(entry){
        cson_object_set(pay, "artifact", entry);
      }
      break;
    }
  }
  if( !disp->name ){
    cson_object_set(pay,"artifact",
                    json_new_string("TODO: handle this artifact type!"));
  }
  veryend:
  blob_reset(&uuid);
  return payV;
}

