/*-
 *   BSD LICENSE
 *
 *   Copyright (c) Intel Corporation.
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "spdk/rpc.h"
#include "spdk/bdev.h"
#include "bdev_longhorn.h"
#include "spdk/util.h"
#include "spdk/string.h"
#include "spdk/log.h"
#include "spdk/env.h"
#include "spdk_internal/lvolstore.h"
#include "../lvol/vbdev_lvol.h"
#include "lib/blob/blobstore.h"

#define RPC_MAX_BASE_BDEVS 255
#define BUFSIZE 255
#define ALIGN_4K 4096

/*
 * Input structure for bdev_longhorn_get_bdevs RPC
 */
struct rpc_bdev_longhorn_get_bdevs {
	/* category - all or online or configuring or offline */
	char *category;
};

/*
 * brief:
 * free_rpc_bdev_longhorn_get_bdevs function frees RPC bdev_longhorn_get_bdevs related parameters
 * params:
 * req - pointer to RPC request
 * returns:
 * none
 */
static void
free_rpc_bdev_longhorn_get_bdevs(struct rpc_bdev_longhorn_get_bdevs *req)
{
	free(req->category);
}

/*
 * Decoder object for RPC get_longhorns
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_get_bdevs_decoders[] = {
	{"category", offsetof(struct rpc_bdev_longhorn_get_bdevs, category), spdk_json_decode_string},
};

/*
 * brief:
 * rpc_bdev_longhorn_get_bdevs function is the RPC for rpc_bdev_longhorn_get_bdevs. This is used to list
 * all the longhorn bdev names based on the input category requested. Category should be
 * one of "all", "online", "configuring" or "offline". "all" means all the longhorns
 * whether they are online or configuring or offline. "online" is the longhorn bdev which
 * is registered with bdev layer. "configuring" is the longhorn bdev which does not have
 * full configuration discovered yet. "offline" is the longhorn bdev which is not
 * registered with bdev as of now and it has encountered any error or user has
 * requested to offline the longhorn.
 * params:
 * request - pointer to json rpc request
 * params - pointer to request parameters
 * returns:
 * none
 */
static void
rpc_bdev_longhorn_get_bdevs(struct spdk_jsonrpc_request *request,
			const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_get_bdevs   req = {};
	struct spdk_json_write_ctx  *w;
	struct longhorn_bdev            *longhorn_bdev;

	if (spdk_json_decode_object(params, rpc_bdev_longhorn_get_bdevs_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_get_bdevs_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "spdk_json_decode_object failed");
		goto cleanup;
	}

	if (!(strcmp(req.category, "all") == 0 ||
	      strcmp(req.category, "online") == 0 ||
	      strcmp(req.category, "configuring") == 0 ||
	      strcmp(req.category, "offline") == 0)) {
		spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
		goto cleanup;
	}

	w = spdk_jsonrpc_begin_result(request);
	spdk_json_write_array_begin(w);

	/* Get longhorn bdev list based on the category requested */
	if (strcmp(req.category, "all") == 0) {
		TAILQ_FOREACH(longhorn_bdev, &g_longhorn_bdev_list, global_link) {
			spdk_json_write_string(w, longhorn_bdev->bdev.name);
		}
	} else if (strcmp(req.category, "online") == 0) {
		TAILQ_FOREACH(longhorn_bdev, &g_longhorn_bdev_configured_list, state_link) {
			spdk_json_write_string(w, longhorn_bdev->bdev.name);
		}
	} else if (strcmp(req.category, "configuring") == 0) {
		TAILQ_FOREACH(longhorn_bdev, &g_longhorn_bdev_configuring_list, state_link) {
			spdk_json_write_string(w, longhorn_bdev->bdev.name);
		}
	} else {
		TAILQ_FOREACH(longhorn_bdev, &g_longhorn_bdev_offline_list, state_link) {
			spdk_json_write_string(w, longhorn_bdev->bdev.name);
		}
	}
	spdk_json_write_array_end(w);
	spdk_jsonrpc_end_result(request, w);

cleanup:
	free_rpc_bdev_longhorn_get_bdevs(&req);
}
SPDK_RPC_REGISTER("bdev_longhorn_get_bdevs", rpc_bdev_longhorn_get_bdevs, SPDK_RPC_RUNTIME)
SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_longhorn_get_bdevs, get_longhorn_bdevs)

struct longhorn_replica {
	char *addr;
	char *lvs;
	uint16_t nvmf_port;
	uint16_t comm_port;
};

/*
 * Base bdevs in RPC bdev_longhorn_create
 */
struct rpc_bdev_longhorn_create_base_bdevs {
	/* Number of base bdevs */
	size_t           num_base_bdevs;

	struct longhorn_replica             replicas[RPC_MAX_BASE_BDEVS];
};

/*
 * Input structure for RPC rpc_bdev_longhorn_create
 */
struct rpc_bdev_longhorn_create {
	/* longhorn bdev name */
	char                                 *name;
	char                                 *address;

	/* Base bdevs information */
	struct rpc_bdev_longhorn_create_base_bdevs base_bdevs;
};

/*
 * brief:
 * free_rpc_bdev_longhorn_create function is to free RPC bdev_longhorn_create related parameters
 * params:
 * req - pointer to RPC request
 * returns:
 * none
 */
static void
free_rpc_bdev_longhorn_create(struct rpc_bdev_longhorn_create *req)
{
	size_t i;

	free(req->name);
	for (i = 0; i < req->base_bdevs.num_base_bdevs; i++) {
		free(req->base_bdevs.replicas[i].addr);
	}
}


/*
 * Decoder object for RPC bdev_longhorn_create
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_create_replica_decoders[] = {

	{"lvs", offsetof(struct longhorn_replica, lvs), spdk_json_decode_string},
	{"addr", offsetof(struct longhorn_replica, addr), spdk_json_decode_string, true},

	{"nvmf_port", offsetof(struct longhorn_replica, nvmf_port), spdk_json_decode_uint16, true},
	{"comm_port", offsetof(struct longhorn_replica, comm_port), spdk_json_decode_uint16, true}
};

static int json_decode_replica(const struct spdk_json_val *val, void *out) {
	int error;
	struct longhorn_replica *replica = out;
	printf("starting json_decode_replica\n");
	printf("type %d\n", val->type);

	error = spdk_json_decode_object(val, rpc_bdev_longhorn_create_replica_decoders, 
				       SPDK_COUNTOF(rpc_bdev_longhorn_create_replica_decoders), out);
	printf("return json_decode_replica: %d\n", error);
	printf("replica lvs: %s\n", replica->lvs);
	printf("replica addr: %s\n", replica->addr);
	printf("replica nvmf port: %u\n", replica->nvmf_port);
	printf("replica comm port: %u\n", replica->comm_port);
	return error;

}
/*
 * Decoder function for RPC bdev_longhorn_create to decode base bdevs list
 */
static int
decode_base_bdevs(const struct spdk_json_val *val, void *out)
{
	struct rpc_bdev_longhorn_create_base_bdevs *base_bdevs = out;
	int error = 0;

	printf("starting decode_base_bdevs\n");
	printf("type %d\n", val->type);
	error = spdk_json_decode_array(val, json_decode_replica, base_bdevs->replicas,
				      RPC_MAX_BASE_BDEVS, &base_bdevs->num_base_bdevs, sizeof(struct longhorn_replica));
	printf("return decode_base_bdevs: %d\n", error);

	return error;
}

/*
 * Decoder object for RPC bdev_longhorn_create
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_create_decoders[] = {
	{"name", offsetof(struct rpc_bdev_longhorn_create, name), spdk_json_decode_string},
	{"address", offsetof(struct rpc_bdev_longhorn_create, address), spdk_json_decode_string, true},
	{"replicas", offsetof(struct rpc_bdev_longhorn_create, base_bdevs), decode_base_bdevs},
};

/*
 * brief:
 * rpc_bdev_longhorn_create function is the RPC for creating RAID bdevs. It takes
 * input as longhorn bdev name and list of base bdev names.
 * params:
 * request - pointer to json rpc request
 * params - pointer to request parameters
 * returns:
 * none
 */
static void
rpc_bdev_longhorn_create(struct spdk_jsonrpc_request *request,
		     const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_create	req = {};
	//struct longhorn_bdev_config		*longhorn_cfg;
	int				rc;
	size_t				i;
	//struct spdk_json_write_ctx *w;

	if (spdk_json_decode_object(params, rpc_bdev_longhorn_create_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_create_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "longhorn spdk_json_decode_object failed");
		goto cleanup;
	}

	rc = longhorn_bdev_create(req.name, req.address, req.base_bdevs.num_base_bdevs);
	if (rc != 0) {
		//longhorn_bdev_config_cleanup(longhorn_cfg);
		spdk_jsonrpc_send_error_response_fmt(request, rc,
						     "Failed to create RAID bdev %s: %s",
						     req.name, spdk_strerror(-rc));
		goto cleanup;
	}

	for (i = 0; i < req.base_bdevs.num_base_bdevs; i++) {

		//longhorn_bdev_add_base_device(req.name, bdev_name);

		longhorn_bdev_add_replica(req.name, req.base_bdevs.replicas[i].lvs, req.base_bdevs.replicas[i].addr, req.base_bdevs.replicas[i].nvmf_port, req.base_bdevs.replicas[i].comm_port, LONGHORN_BASE_BDEV_RW);

	}



	spdk_jsonrpc_send_bool_response(request, true);




cleanup:
	free_rpc_bdev_longhorn_create(&req);
}
SPDK_RPC_REGISTER("longhorn_volume_create", rpc_bdev_longhorn_create, SPDK_RPC_RUNTIME)
SPDK_RPC_REGISTER_ALIAS_DEPRECATED(longhorn_volume_create, bdev_longhorn_create)
SPDK_RPC_REGISTER_ALIAS_DEPRECATED(longhorn_volume_create, construct_longhorn_bdev)


/*
 * Input structure for RPC rpc_bdev_longhorn_create
 */
struct rpc_bdev_longhorn_add_replica {
	/* Raid bdev name */
	char                                 *name;

	/* Base bdevs information */
	struct longhorn_replica replica;
};

/*
 * Decoder object for RPC bdev_longhorn_create
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_add_replica_decoders[] = {
	{"name", offsetof(struct rpc_bdev_longhorn_add_replica, name), spdk_json_decode_string},
	{"replica", offsetof(struct rpc_bdev_longhorn_add_replica, replica), json_decode_replica},
};

/*
 * brief:
 * rpc_bdev_longhorn_create function is the RPC for creating RAID bdevs. It takes
 * input as longhorn bdev name and list of base bdev names.
 * params:
 * request - pointer to json rpc request
 * params - pointer to request parameters
 * returns:
 * none
 */
static void
rpc_bdev_longhorn_add_replica_cmd(struct spdk_jsonrpc_request *request,
		     const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_add_replica	req = {};
	//struct longhorn_bdev_config		*longhorn_cfg;
	int				rc;
	size_t				i;
	char *bdev_name;

	printf("type %d\n", params->type);
	if (spdk_json_decode_object(params, rpc_bdev_longhorn_add_replica_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_add_replica_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "longhorn spdk_json_decode_object failed");
	}

	rc = longhorn_volume_add_replica(req.name, req.replica.lvs, req.replica.addr, req.replica.nvmf_port, req.replica.comm_port);

	if (rc != 0) {
                spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "failed to add replica");
        } else {
                spdk_jsonrpc_send_bool_response(request, true);

        }

}

SPDK_RPC_REGISTER("longhorn_volume_add_replica", rpc_bdev_longhorn_add_replica_cmd, SPDK_RPC_RUNTIME)

static void
rpc_bdev_longhorn_remove_replica_cmd(struct spdk_jsonrpc_request *request,
		     const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_add_replica	req = {};
	//struct longhorn_bdev_config		*longhorn_cfg;
	int				rc;
	size_t				i;
	char *bdev_name;

	printf("type %d\n", params->type);
	if (spdk_json_decode_object(params, rpc_bdev_longhorn_add_replica_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_add_replica_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "longhorn spdk_json_decode_object failed");
		return;
	}

	rc = longhorn_bdev_remove_replica(req.name, req.replica.lvs, req.replica.addr, req.replica.nvmf_port, req.replica.comm_port);


	if (rc != 0) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "failed to remove replica");
	} else {
		spdk_jsonrpc_send_bool_response(request, true);

	}
}

SPDK_RPC_REGISTER("longhorn_volume_remove_replica", rpc_bdev_longhorn_remove_replica_cmd, SPDK_RPC_RUNTIME)


/*
 * Input structure for RPC deleting a longhorn bdev
 */
struct rpc_bdev_longhorn_delete {
	/* longhorn bdev name */
	char *name;
};

/*
 * brief:
 * free_rpc_bdev_longhorn_delete function is used to free RPC bdev_longhorn_delete related parameters
 * params:
 * req - pointer to RPC request
 * params:
 * none
 */
static void
free_rpc_bdev_longhorn_delete(struct rpc_bdev_longhorn_delete *req)
{
	free(req->name);
}

/*
 * Decoder object for RPC longhorn_bdev_delete
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_delete_decoders[] = {
	{"name", offsetof(struct rpc_bdev_longhorn_delete, name), spdk_json_decode_string},
};

struct rpc_bdev_longhorn_delete_ctx {
	struct rpc_bdev_longhorn_delete req;
	//struct longhorn_bdev_config *longhorn_cfg;
	struct spdk_jsonrpc_request *request;
};

/*
 * brief:
 * params:
 * cb_arg - pointer to the callback context.
 * rc - return code of the deletion of the longhorn bdev.
 * returns:
 * none
 */
static void
bdev_longhorn_delete_done(void *cb_arg, int rc)
{
	struct rpc_bdev_longhorn_delete_ctx *ctx = cb_arg;
//	struct longhorn_bdev_config *longhorn_cfg;
	struct spdk_jsonrpc_request *request = ctx->request;

	if (rc != 0) {
		SPDK_ERRLOG("Failed to delete longhorn bdev %s (%d): %s\n",
			    ctx->req.name, rc, spdk_strerror(-rc));
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 spdk_strerror(-rc));
		goto exit;
	}

#if 0
	longhorn_cfg = ctx->longhorn_cfg;
	assert(longhorn_cfg->longhorn_bdev == NULL);

	longhorn_bdev_config_cleanup(longhorn_cfg);
#endif

	spdk_jsonrpc_send_bool_response(request, true);
exit:
	free_rpc_bdev_longhorn_delete(&ctx->req);
	free(ctx);
}

/*
 * brief:
 * rpc_bdev_longhorn_delete function is the RPC for deleting a longhorn bdev. It takes longhorn
 * name as input and delete that longhorn bdev including freeing the base bdev
 * resources.
 * params:
 * request - pointer to json rpc request
 * params - pointer to request parameters
 * returns:
 * none
 */
static void
rpc_bdev_longhorn_delete(struct spdk_jsonrpc_request *request,
		     const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_delete_ctx *ctx;

	ctx = calloc(1, sizeof(*ctx));
	if (!ctx) {
		spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
		return;
	}

	if (spdk_json_decode_object(params, rpc_bdev_longhorn_delete_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_delete_decoders),
				    &ctx->req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "spdk_json_decode_object failed");
		goto cleanup;
	}
 
#if 0
	ctx->longhorn_cfg = longhorn_bdev_config_find_by_name(ctx->req.name);
	if (ctx->longhorn_cfg == NULL) {
		spdk_jsonrpc_send_error_response_fmt(request, ENODEV,
						     "longhorn bdev %s is not found in config",
						     ctx->req.name);
		goto cleanup;
	}
#endif

	ctx->request = request;

	/* Remove all the base bdevs from this longhorn bdev before deleting the longhorn bdev */
	//longhorn_bdev_remove_base_devices(ctx->longhorn_cfg, bdev_longhorn_delete_done, ctx);

	return;

cleanup:
	free_rpc_bdev_longhorn_delete(&ctx->req);
	free(ctx);
}
SPDK_RPC_REGISTER("bdev_longhorn_delete", rpc_bdev_longhorn_delete, SPDK_RPC_RUNTIME)
SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_longhorn_delete, destroy_longhorn_bdev)

struct cluster_entry {
	int cluster;

	TAILQ_ENTRY(cluster_entry) entries;
};

struct read_sparse_context {
	struct spdk_blob_store *blobstore;
	struct spdk_blob *blob;
	struct spdk_io_channel *channel;
	const char *name;
	uint64_t id;
	uint64_t num_clusters;
	uint32_t cluster_size;
	uint32_t io_unit_size;
	uint64_t current_cluster;
	uint64_t allocated_clusters;
	uint64_t start_offset;

	FILE *fp;
	uint8_t *cluster;

	TAILQ_HEAD(, cluster_entry)  cluster_head;
	struct cluster_entry *current;
};

static void 
read_cluster_cb(void *arg1, int bserrno);
static void blob_it_cb(void *arg1, struct spdk_blob *blob, int bserrno);

static void
read_next_allocated_cluster(struct read_sparse_context *ctx) {
	uint64_t offset;

	if (ctx->current) {
		offset = ctx->current->cluster * ctx->cluster_size / ctx->io_unit_size;

		printf("reading at %" PRIu64 ":%" PRIu64 "\n", offset, ctx->cluster_size);

		spdk_blob_io_read(ctx->blob, ctx->channel, ctx->cluster, offset,
				  ctx->cluster_size / ctx->io_unit_size, read_cluster_cb, ctx);
	} else {
		fclose(ctx->fp);
	}
}

static void 
read_cluster_cb(void *arg1, int bserrno)
{
	struct read_sparse_context *ctx = arg1;
	uint32_t nwritten;

	if (bserrno) {
		printf("error: %d\n", bserrno);
		fclose(ctx->fp);
	        spdk_bs_iter_next(ctx->blobstore, ctx->blob, blob_it_cb, ctx->blobstore);
		return;
	}

	printf("successful read\n");

	nwritten = fwrite(ctx->cluster, 1, ctx->cluster_size, ctx->fp);
	printf("nwritten %u ? %u\n", nwritten, ctx->cluster_size);
	
	if (nwritten != ctx->cluster_size) {
		printf("nwritten not euqal to cluster size\n");
		fclose(ctx->fp);
	        spdk_bs_iter_next(ctx->blobstore, ctx->blob, blob_it_cb, ctx->blobstore);
		return;
	}


	if (++ctx->current_cluster < ctx->allocated_clusters && ctx->current != NULL) {

		struct cluster_entry *tmp = TAILQ_NEXT(ctx->current, entries);
		printf("next cluster = %d\n", tmp->cluster);
		ctx->current = tmp;

		read_next_allocated_cluster(ctx);
	} else {

		printf("complete\n");
		fclose(ctx->fp);
		//spdk_blob_close(ctx->blob, close_cb, ctx);
	        spdk_bs_iter_next(ctx->blobstore, ctx->blob, blob_it_cb, ctx->blobstore);

	}


}


static void open_file(struct read_sparse_context *ctx)
{
	char filename[BUFSIZE] = {'\0'};
	uint64_t blocks;
	struct cluster_entry *entry;
	long offset;

	if (ctx->name) {
		snprintf(filename, BUFSIZE - 1, "%s.kdat", ctx->name);
	} else {
		snprintf(filename, BUFSIZE - 1, "%" PRIx64 ".kdat", ctx->id);
	}

	printf("opening %s\n", filename);
	ctx->fp = fopen(filename, "w");

	if (ctx->fp) {
		fwrite(&ctx->num_clusters, sizeof (uint64_t), 1, ctx->fp);
		fwrite(&ctx->allocated_clusters, sizeof (uint64_t), 1, ctx->fp);
		fwrite(&ctx->cluster_size, sizeof (uint32_t), 1, ctx->fp);
		fwrite(&ctx->io_unit_size, sizeof (uint32_t), 1, ctx->fp);

		TAILQ_FOREACH(entry, &ctx->cluster_head, entries) {
			fwrite(&entry->cluster, sizeof (int), 1, ctx->fp);
		}

		
		offset = ftell(ctx->fp);

		ctx->start_offset = offset;

		if (offset % ctx->io_unit_size != 0) {
			ctx->start_offset = ((offset / ctx->io_unit_size) + 1) *
				ctx->io_unit_size;
		}
		
		fseek(ctx->fp, ctx->start_offset, SEEK_SET);

		ctx->cluster = spdk_malloc(ctx->cluster_size,  ALIGN_4K, NULL,
                                        SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);

		ctx->channel = spdk_bs_alloc_io_channel(ctx->blobstore);

		ctx->current = TAILQ_FIRST(&ctx->cluster_head);

		read_next_allocated_cluster(ctx);
	}

}


static void blob_it_cb(void *arg1, struct spdk_blob *blob, int bserrno)
{
	struct spdk_blob_store *blobstore = arg1;
	uint64_t val;
	struct spdk_xattr_names *names;
	const void *value;
	size_t value_len;
	unsigned int i;
	struct cluster_entry *entry;
	struct read_sparse_context *ctx = NULL;
	char *xattr_name = NULL;

	if (bserrno) {
		if (bserrno == -ENOENT) {
			printf("last blob\n");
		} else {
			printf("error blob: %d\n", bserrno);
		}
		return;
	}
	ctx = calloc(1, sizeof(struct read_sparse_context));

	printf("Blob ID: %" PRIx64 "\n", spdk_blob_get_id(blob));
	printf("Blob Parent ID: %" PRIx64 "\n", blob->parent_id);

        val = spdk_blob_get_num_clusters(blob);
        printf("# of clusters: %" PRIu64 "\n", val);
	printf("cluster size: %d\n", blobstore->cluster_sz);
	printf("io unit size: %d\n", blobstore->io_unit_size);

	ctx->id = spdk_blob_get_id(blob);
	ctx->num_clusters = blob->active.num_clusters;
	ctx->cluster_size = blobstore->cluster_sz;
	ctx->io_unit_size = blobstore->io_unit_size;
	ctx->blobstore = blobstore;
	ctx->blob = blob;

        val = spdk_blob_get_num_pages(blob);
        printf("# of pages: %" PRIu64 "\n", val);
        printf("# of pages per cluster: %" PRIu64 "\n", blobstore->pages_per_cluster);

        spdk_blob_get_xattr_names(blob, &names);

        printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names));
        printf("xattrs:\n");
        for (i = 0; i < spdk_xattr_names_get_count(names); i++) {
		xattr_name = spdk_xattr_names_get_name(names, i);

                spdk_blob_get_xattr_value(blob, xattr_name,
                                          &value, &value_len);
                if (value_len > BUFSIZE) {
                        printf("FYI: adjusting size of xattr due to CLI limits.\n");
                        value_len = BUFSIZE + 1;
                }
		
		if (strcmp(xattr_name, "name") == 0) {
			ctx->name = strdup(value);
		}

                printf("\n(%d) Name:%s\n", i, xattr_name);
                printf("(%d) Value:\n", i);
                spdk_log_dump(stdout, "", value, value_len - 1);

        }


	TAILQ_INIT(&ctx->cluster_head);
	for (i = 0; i < blob->active.num_clusters; ++i) {
		if (blob->active.clusters[i] != 0) {
			entry = malloc(sizeof(struct cluster_entry));
			entry->cluster = i;
			TAILQ_INSERT_TAIL(&ctx->cluster_head, entry, entries);
			printf("Cluster %d LBA: %"  PRIu64 "\n", i, blob->active.clusters[i]);
			++ctx->allocated_clusters;
		}
	}

	printf("Allocated clusters: %d\n", ctx->allocated_clusters);
	TAILQ_FOREACH(entry, &ctx->cluster_head, entries) {
		printf("Cluster %d\n", entry->cluster);

	}

	if (ctx->allocated_clusters > 0) {
		open_file(ctx);
	} else {
		free(ctx);
	        spdk_bs_iter_next(blobstore, blob, blob_it_cb, blobstore);
	}



}

struct rpc_lvol_show_blobs_param {
	char *lvs;
};

static const struct spdk_json_object_decoder rpc_lvol_show_blobs_decoders[] = {
	{"lvs", offsetof(struct rpc_lvol_show_blobs_param, lvs), spdk_json_decode_string},
};

static void spdk_bsdump_done(void *arg, int bserrno) {
}
static void
bsdump_print_xattr(FILE *fp, const char *bstype, const char *name, const void *value,
                   size_t value_len)
{
        if (strncmp(bstype, "BLOBFS", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
                if (strcmp(name, "name") == 0) {
                        fprintf(fp, "%.*s", (int)value_len, (char *)value);
                } else if (strcmp(name, "length") == 0 && value_len == sizeof(uint64_t)) {
                        uint64_t length;

                        memcpy(&length, value, sizeof(length));
                        fprintf(fp, "%" PRIu64, length);
                } else {
                        fprintf(fp, "?");
                }
        } else if (strncmp(bstype, "LVOLSTORE", SPDK_BLOBSTORE_TYPE_LENGTH) == 0) {
                if (strcmp(name, "name") == 0) {
                        fprintf(fp, "%s", (char *)value);
                } else if (strcmp(name, "uuid") == 0 && value_len == sizeof(struct spdk_uuid)) {
                        char uuid[SPDK_UUID_STRING_LEN];

                        spdk_uuid_fmt_lower(uuid, sizeof(uuid), (struct spdk_uuid *)value);
                        fprintf(fp, "%s", uuid);
                } else {
                        fprintf(fp, "?");
                }
        } else {
                fprintf(fp, "?");
        }
}

static void rpc_lvol_show_blobs(struct spdk_jsonrpc_request *request,
                     const struct spdk_json_val *params)
{
	struct spdk_lvol_store *lvs;
	struct lvol_store_bdev *lvs_bdev;
	struct rpc_lvol_show_blobs_param req;

#if 0

	if (spdk_json_decode_object(params, rpc_lvol_show_blobs_decoders,
				    SPDK_COUNTOF(rpc_lvol_show_blobs_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "spdk_json_decode_object failed");
		return;
	}
#endif

	//lvs = vbdev_get_lvol_store_by_name(req.lvs);
	lvs_bdev = vbdev_lvol_store_first();
	lvs = lvs_bdev->lvs;


	while (lvs_bdev != NULL) {
		lvs = lvs_bdev->lvs;

		spdk_bs_iter_first(lvs->blobstore, blob_it_cb, lvs->blobstore);
		 lvs_bdev = vbdev_lvol_store_next(lvs_bdev);
		 //spdk_bs_dump(lvs->blobstore->dev, stdout, bsdump_print_xattr, spdk_bsdump_done, NULL);
	}

		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "egg");
}

SPDK_RPC_REGISTER("lvol_show_blobs", rpc_lvol_show_blobs, SPDK_RPC_RUNTIME)

struct rpc_bdev_longhorn_snapshot {
	char *name;
	char *snapshot;
};

static void
free_rpc_bdev_longhorn_snapshot(struct rpc_bdev_longhorn_snapshot *req)
{
	free(req->name);
	free(req->snapshot);
}

/*
 * Decoder object for RPC longhorn_bdev_delete
 */
static const struct spdk_json_object_decoder rpc_bdev_longhorn_snapshot_decoders[] = {
	{"name", offsetof(struct rpc_bdev_longhorn_snapshot, name), spdk_json_decode_string},
	{"snapshot", offsetof(struct rpc_bdev_longhorn_snapshot, snapshot), spdk_json_decode_string},
};

struct rpc_bdev_longhorn_snapshot_ctx {
	char *name;
	char *snapshot;
	struct spdk_jsonrpc_request *request;
};

static void rpc_longhorn_snapshot_complete(void *cb_arg) {
	struct rpc_bdev_longhorn_snapshot_ctx *ctx = cb_arg;
}



static void rpc_longhorn_snapshot_cmd(struct spdk_jsonrpc_request *request, 
				      const struct spdk_json_val *params)
{
	struct rpc_bdev_longhorn_snapshot req;
	struct rpc_bdev_longhorn_snapshot_ctx *ctx;
	//struct longhorn_bdev_config *longhorn_cfg;

	if (spdk_json_decode_object(params, rpc_bdev_longhorn_snapshot_decoders,
				    SPDK_COUNTOF(rpc_bdev_longhorn_snapshot_decoders),
				    &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "spdk_json_decode_object failed");
		return;
	}


#if 0
	longhorn_cfg = longhorn_bdev_config_find_by_name(req.name);
	if (longhorn_cfg == NULL) {
		spdk_jsonrpc_send_error_response_fmt(request, ENODEV,
						     "longhorn bdev %s is not found in config",
						     req.name);
//goto cleanup;
	}
#endif


	ctx = calloc(1, sizeof(*ctx));
	




}

SPDK_RPC_REGISTER("bdev_longhorn_snapshot", rpc_longhorn_snapshot_cmd, SPDK_RPC_RUNTIME)


