/**
 * The is the proxy callback function. This will be passed to the old simple-scan
 * mechanism. When this gets called, it will create an as_val structure out of the
 * record and will call the callback that user supplied (folded into the udata structure)
 */
static int simplescan_cb(char *ns, cf_digest *keyd, char *set, cl_object *key,
		int result, uint32_t generation, uint32_t record_void_time,
		cl_bin *bins, uint16_t n_bins, void *udata)
{
	scan_bridge * bridge = (scan_bridge *) udata;

	// Fill the bin data
	as_record _rec, * rec = &_rec;
	as_record_inita(rec, n_bins);
	clbins_to_asrecord(bins, (uint32_t)n_bins, rec);

	// Fill the metadata
	askey_from_clkey(&rec->key, ns, set, key);
	memcpy(rec->key.digest.value, keyd, sizeof(cf_digest));
	rec->key.digest.init = true;
	rec->gen = generation;
	rec->ttl = record_void_time;

	// Call the callback that user wanted to callback
	bool rv = bridge->callback((as_val *) rec, bridge->udata);

	// The responsibility to free the bins is on the called callback function
	// In scan case, only LIST & MAP will have an active free
	citrusleaf_bins_free(bins, (int)n_bins);

	// release the record
	as_record_destroy(rec);

	return rv ? 0 : 1;
}
static int
cl_batch_cb(char *ns, cf_digest *keyd, char *set, cl_object *key, int result,
		uint32_t generation, uint32_t ttl, cl_bin *bins, uint16_t n_bins,
		void *udata)
{
	batch_bridge * p_bridge = (batch_bridge *) udata;
	aerospike * as = p_bridge->as;
	as_batch_read * p_r = NULL;

	// Find the digest.
	for (uint32_t i = 0; i < p_bridge->n; i++) {
		p_r = &p_bridge->results[i];

		if (memcmp(keyd, p_r->key->digest.value, AS_DIGEST_VALUE_SIZE) == 0) {
			// Not bothering to check set, which is not always filled.
			break;
		}

		p_r = NULL;
	}

	if (! p_r) {
		as_err(LOGGER, "couldn't find digest");
		return -1; // not that this is even checked...
	}

	// Fill out this result slot.
	as_error err;
	p_r->result = as_error_fromrc(&err, result);

	// If the result wasn't success, we won't have any record data or metadata.
	if (result != 0) {
		return 0;
	}

	as_record_init(&p_r->record, n_bins); // works even if n_bins is 0

	// There should be record metadata.
	p_r->record.gen = (uint16_t)generation;
	p_r->record.ttl = ttl;

	// There may be bin data.
	if (n_bins != 0) {
		clbins_to_asrecord(bins, (uint32_t)n_bins, &p_r->record);
	}

	return 0;
}
/**
 *	Look up a record by key, then return all bins.
 *	
 *	@param as			The aerospike instance to use for this operation.
 *	@param err			The as_error to be populated if an error occurs.
 *	@param policy		The policy to use for this operation. If NULL, then the default policy will be used.
 *	@param key			The key of the record.
 *	@param rec 			The record to be populated with the data from request.
 *
 *	@return AEROSPIKE_OK if successful. Otherwise an error.
 */
as_status aerospike_key_get(
	aerospike * as, as_error * err, const as_policy_read * policy, 
	const as_key * key, 
	as_record ** rec) 
{
	// we want to reset the error so, we have a clean state
	as_error_reset(err);
	
	// resolve policies
	as_policy_read p;
	as_policy_read_resolve(&p, &as->config.policies, policy);

	uint32_t    timeout = p.timeout == UINT32_MAX ? 0 : p.timeout;          
	uint32_t    gen = 0;
	uint32_t 	ttl = 0;
	int         nvalues = 0;
	cl_bin *    values = NULL;

	cl_rv rc = CITRUSLEAF_OK;

	switch ( p.key ) {
		case AS_POLICY_KEY_DIGEST: {
			as_digest * digest = as_key_digest((as_key *) key);
			rc = citrusleaf_get_all_digest_getsetname(as->cluster, key->ns, (cf_digest *) digest->value,
					&values, &nvalues, timeout, &gen, NULL, &ttl);
			break;
		}
		case AS_POLICY_KEY_SEND: {
			cl_object okey;
			asval_to_clobject((as_val *) key->valuep, &okey);
			as_digest * digest = as_key_digest((as_key *) key);
			rc = citrusleaf_get_all(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value,
					&values, &nvalues, timeout, &gen, &ttl);
			break;
		}
		default: {
			// ERROR CASE
			break;
		}
	}
	
	if ( rc == CITRUSLEAF_OK && rec != NULL ) {
		as_record * r = *rec;
		if ( r == NULL ) {
			r = as_record_new(0);
		}
		if ( r->bins.entries == NULL ) {
			r->bins.capacity = nvalues;
			r->bins.size = 0;
			r->bins.entries = malloc(sizeof(as_bin) * nvalues);
			r->bins._free = true;
		}
		clbins_to_asrecord(values, nvalues, r);
		r->gen = (uint16_t) gen;
		r->ttl = ttl;
		*rec = r;
	}

	if ( values != NULL ) {
		// We are freeing the bins' objects, as opposed to bins themselves.
		citrusleaf_bins_free(values, nvalues);
		free(values);
	}

	return as_error_fromrc(err,rc);
}
/**
 *	Lookup a record by key, then return specified bins.
 *	
 *	@param as			The aerospike instance to use for this operation.
 *	@param err			The as_error to be populated if an error occurs.
 *	@param policy		The policy to use for this operation. If NULL, then the default policy will be used.
 *	@param key			The key of the record.
 *	@param bins			The bins to select. A NULL terminated array of NULL terminated strings.
 *	@param rec 			The record to be populated with the data from request.
 *
 *	@return AEROSPIKE_OK if successful. Otherwise an error.
 */
as_status aerospike_key_select(
	aerospike * as, as_error * err, const as_policy_read * policy, 
	const as_key * key, const char * bins[], 
	as_record ** rec) 
{
	// we want to reset the error so, we have a clean state
	as_error_reset(err);
	
	// resolve policies
	as_policy_read p;
	as_policy_read_resolve(&p, &as->config.policies, policy);

	uint32_t    timeout = p.timeout == UINT32_MAX ? 0 : p.timeout;
	uint32_t    gen = 0;
	uint32_t 	ttl = 0;
	int         nvalues = 0;
	cl_bin *    values = NULL;

	for (nvalues = 0; bins[nvalues] != NULL && bins[nvalues][0] != '\0'; nvalues++)
		;

	values = (cl_bin *) alloca(sizeof(cl_bin) * nvalues);
	for ( int i = 0; i < nvalues; i++ ) {
		if ( strlen(bins[i]) > AS_BIN_NAME_MAX_LEN ) {
			return as_error_update(err, AEROSPIKE_ERR_PARAM, "bin name too long: %s", bins[i]);
		}

		strcpy(values[i].bin_name, bins[i]);
		citrusleaf_object_init(&values[i].object);
	}

	cl_rv rc = CITRUSLEAF_OK;

	switch ( p.key ) {
		case AS_POLICY_KEY_DIGEST: {
			as_digest * digest = as_key_digest((as_key *) key);
			rc = citrusleaf_get_digest(as->cluster, key->ns, (cf_digest *) digest->value,
					values, nvalues, timeout, &gen, &ttl);
			break;
		}
		case AS_POLICY_KEY_SEND: {
			cl_object okey;
			asval_to_clobject((as_val *) key->valuep, &okey);
			as_digest * digest = as_key_digest((as_key *) key);
			rc = citrusleaf_get(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value,
					values, nvalues, timeout, &gen, &ttl);
			break;
		}
		default: {
			// ERROR CASE
			break;
		}
	}

	if ( rc == CITRUSLEAF_OK && rec != NULL ) {
		as_record * r = *rec;
		if ( r == NULL ) {
			r = as_record_new(0);
		}
		if ( r->bins.entries == NULL ) {
			r->bins.capacity = nvalues;
			r->bins.size = 0;
			r->bins.entries = malloc(sizeof(as_bin) * nvalues);
			r->bins._free = true;
		}
		clbins_to_asrecord(values, nvalues, r);
		r->gen = (uint16_t) gen;
		r->ttl = ttl;
		*rec = r;
	}

	if ( values != NULL ) {
		// We are freeing the bins' objects, as opposed to bins themselves.
		citrusleaf_bins_free(values, nvalues);
	}

	return as_error_fromrc(err,rc);
}