/*******************************************************************************
 * Copyright 2013-2017 Aerospike, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

#include <Python.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <aerospike/aerospike_key.h>
#include <aerospike/as_key.h>
#include <aerospike/as_error.h>
#include <aerospike/as_record.h>
#include <aerospike/as_operations.h>
#include <aerospike/aerospike_info.h>
#include "client.h"
#include "conversions.h"
#include "exceptions.h"
#include "policy.h"
#include "serializer.h"
#include "geo.h"

#include <aerospike/as_double.h>
#include <aerospike/as_integer.h>
#include <aerospike/as_geojson.h>
#include <aerospike/as_nil.h>

#define BASE_VARIABLES\
	as_error err;\
	as_error_init(&err);\
	PyObject * py_key = NULL;\
	PyObject * py_bin = NULL;\
	PyObject * py_policy = NULL;\
	PyObject * py_meta = NULL;\
	as_policy_operate operate_policy;\
	as_policy_operate *operate_policy_p = NULL;\
	as_key key;\
	bool key_created = false;\
	char* bin = NULL;\

#define CHECK_CONNECTED_AND_CDT_SUPPORT()\
	if (!self || !self->as) {\
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");\
		goto CLEANUP;\
	}\
	if (!self->is_conn_16) {\
		as_error_update(&err, AEROSPIKE_ERR_CLUSTER, "No connection to aerospike cluster");\
		goto CLEANUP;\
	}\
	if (!has_cdt_list(self->as, &err)) {\
		as_error_update(&err, AEROSPIKE_ERR_UNSUPPORTED_FEATURE, "CDT list feature is not supported");\
		goto CLEANUP;\
	}\

#define POLICY_KEY_META_BIN()\
	if (py_policy) {\
		if (pyobject_to_policy_operate(&err, py_policy, &operate_policy, &operate_policy_p,\
				&self->as->config.policies.operate) != AEROSPIKE_OK) {\
			goto CLEANUP;\
		}\
	}\
	if (pyobject_to_key(&err, py_key, &key) != AEROSPIKE_OK) {\
		goto CLEANUP;\
	} else {\
		key_created = true;\
	}\
	if (py_meta) {\
		if (check_for_meta(py_meta, &ops, &err) != AEROSPIKE_OK) {\
			goto CLEANUP;\
		}\
	}\
	if (bin_strict_type_checking(self, &err, py_bin, &bin) != AEROSPIKE_OK) {\
		goto CLEANUP;\
	}\

#define DO_OPERATION(__rec)\
	Py_BEGIN_ALLOW_THREADS\
	aerospike_key_operate(self->as, &err, operate_policy_p, &key, &ops, __rec);\
	Py_END_ALLOW_THREADS

#define EXCEPTION_ON_ERROR()\
	if (key_created) {\
		as_key_destroy(&key);\
	}\
	if (err.code != AEROSPIKE_OK) {\
		PyObject * py_err = NULL;\
		error_to_pyobject(&err, &py_err);\
		PyObject *exception_type = raise_exception(&err);\
		if (PyObject_HasAttrString(exception_type, "key")) {\
			PyObject_SetAttrString(exception_type, "key", py_key);\
		}\
		if (PyObject_HasAttrString(exception_type, "bin")) {\
			PyObject_SetAttrString(exception_type, "bin", py_bin);\
		}\
		PyErr_SetObject(exception_type, py_err);\
		Py_DECREF(py_err);\
		return NULL;\
	}

 /**
  *******************************************************************************************************
  * Check whether Aerospike server supports CDT feature or not.
  *******************************************************************************************************
  */
 #define INFO_CALL "features"
 static bool has_cdt_list(aerospike *as, as_error *err)
 {
 	char *res = NULL;

 	int rc = aerospike_info_any(as, err, NULL, INFO_CALL, &res);

 	if (rc == AEROSPIKE_OK) {
 		char *st = strstr(res, "cdt-list");
 		free(res);
 		if (st) {
 			return true;
 		}
 	}
 	return false;
 }


/**
 *******************************************************************************************************
 * Append a single value to the list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an integer status. 0(Zero) is success value.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListAppend(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_append_val = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);

	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "val", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOO|OO:list_append", kwlist,
				&py_key, &py_bin, &py_append_val, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	POLICY_KEY_META_BIN();

	as_val* put_val = NULL;
	if (pyobject_to_astype_write(self, &err, py_append_val, &put_val,
			&static_pool, SERIALIZER_PYTHON) != AEROSPIKE_OK) {
		goto CLEANUP;
	}
	as_operations_add_list_append(&ops, bin, put_val);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Extend the list value in bin with the given items.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an integer status. 0(Zero) is success value.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListExtend(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_append_val = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);

	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "items", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOO|OO:list_extend", kwlist,
				&py_key, &py_bin, &py_append_val, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	if (!PyList_Check(py_append_val)) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Items should be of type list");
		goto CLEANUP;
	}

	POLICY_KEY_META_BIN();

	as_val* put_val = NULL;
	pyobject_to_astype_write(self, &err, py_append_val, &put_val,
			&static_pool, SERIALIZER_PYTHON);
	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}
	as_operations_add_list_append_items(&ops, bin, (as_list*) put_val);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Inserts val at the specified index of the list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an integer status. 0(Zero) is success value.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListInsert(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_insert_val = NULL;
	uint64_t index;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "val", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOlO|OO:list_insert", kwlist,
				&py_key, &py_bin, &index, &py_insert_val, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	POLICY_KEY_META_BIN();

	as_val* put_val = NULL;
	pyobject_to_astype_write(self, &err, py_insert_val, &put_val,
			&static_pool, SERIALIZER_PYTHON);
	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	as_operations_add_list_insert(&ops, bin, index, put_val);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Insert the items at the specified index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an integer status. 0(Zero) is success value.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListInsertItems(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_insert_val = NULL;
	uint64_t index;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "items", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOlO|OO:list_insert_items", kwlist,
				&py_key, &py_bin, &index, &py_insert_val, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	if (!PyList_Check(py_insert_val)) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Items should be of type list");
		goto CLEANUP;
	}

	POLICY_KEY_META_BIN();

	as_val* put_val = NULL;
	pyobject_to_astype_write(self, &err, py_insert_val, &put_val,
			&static_pool, SERIALIZER_PYTHON);
	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	as_operations_add_list_insert_items(&ops, bin, index, (as_list*) put_val);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Count the elements of the list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns count of elements in the list.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListSize(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	int64_t list_size = 0;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);

	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:list_size", kwlist,
				&py_key, &py_bin, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_size(&ops, bin);

	// Initialize record
	as_record_init(rec, 0);

	DO_OPERATION(&rec);

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	if (rec) {
		list_size = as_record_get_int64(rec, bin, 0);
	}

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(list_size);
}

/**
 *******************************************************************************************************
 * Remove and get back a list element at a given index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an element at that index.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListPop(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	uint64_t index;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOl|OO:list_pop", kwlist,
				&py_key, &py_bin, &index, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_pop(&ops, bin, index);

	DO_OPERATION(&rec);

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}
	PyObject *py_val = NULL;
	if (rec && rec->bins.size) {
		val_to_pyobject(self, &err, (as_val*) (rec->bins.entries[0].valuep), &py_val);
	} else {
		py_val = Py_None;
		Py_INCREF(py_val);
	}

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}
	EXCEPTION_ON_ERROR();

	return py_val;
}

/**
 *******************************************************************************************************
 * Remove and get back list elements at a given index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListPopRange(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_list = NULL;
	uint64_t index;
	uint64_t count = -1;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "count", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOll|OO:list_pop_range", kwlist,
				&py_key, &py_bin, &index, &count, &py_meta, &py_policy) == false) {
		return NULL;
	}

	if (!self || !self->as) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
		goto CLEANUP;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_pop_range(&ops, bin, index, count);

	DO_OPERATION(&rec);

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}
	if (rec && rec->bins.size) {
		list_to_pyobject(self, &err, as_record_get_list(rec, bin), &py_list);
	} else {
		py_list = Py_None;
		Py_INCREF(py_list);
	}

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}
	EXCEPTION_ON_ERROR();

	return py_list;
}

/**
 *******************************************************************************************************
 * Remove a list element at a given index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListRemove(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	uint64_t index;
	as_operations ops;
	as_operations_inita(&ops, 1);

	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOl|OO:list_remove", kwlist,
				&py_key, &py_bin, &index, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_remove(&ops, bin, index);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Remove list elements at a given index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListRemoveRange(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	uint64_t index;
	uint64_t count = -1;
	as_operations ops;
	as_operations_inita(&ops, 1);

	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "count", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOll|OO:list_remove_range", kwlist,
				&py_key, &py_bin, &index, &count, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_remove_range(&ops, bin, index, count);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Remove all the elements from a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListClear(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:list_clear", kwlist,
				&py_key, &py_bin, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_clear(&ops, bin);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Set list element val at the specified index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListSet(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_val = NULL;
	uint64_t index;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "val", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOlO|OO:list_set", kwlist,
				&py_key, &py_bin, &index, &py_val, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	POLICY_KEY_META_BIN();

	as_val* put_val = NULL;
	pyobject_to_astype_write(self, &err, py_val, &put_val,
			&static_pool, SERIALIZER_PYTHON);
	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}
	as_operations_add_list_set(&ops, bin, index, put_val);

	DO_OPERATION(NULL);

CLEANUP:
	as_operations_destroy(&ops);
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

/**
 *******************************************************************************************************
 * Get the list element at the specified index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListGet(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	uint64_t index;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "meta", "policy", NULL};
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOl|OO:list_get", kwlist,
				&py_key, &py_bin, &index, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_get(&ops, bin, index);

	DO_OPERATION(&rec);

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	PyObject *py_val = NULL;
	if (rec && rec->bins.size) {
		val_to_pyobject(self, &err, (as_val*) (rec->bins.entries[0].valuep), &py_val);
	} else {
		py_val = Py_None;
		Py_INCREF(py_val);
	}

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}

	EXCEPTION_ON_ERROR();

	return py_val;
}

/**
 *******************************************************************************************************
 * Get the list of count elements starting at a specified index of a list value in bin.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns a list of elements.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListGetRange(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	PyObject * py_list = NULL;
	uint64_t index;
	uint64_t count;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "count", "meta", "policy", NULL};

	// Python Function Argument Parsing
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOll|OO:list_get_range", kwlist,
				&py_key, &py_bin, &index, &count, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_get_range(&ops, bin, index, count);

	DO_OPERATION(&rec);

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	if (rec && rec->bins.size) {
		list_to_pyobject(self, &err, as_record_get_list(rec, bin), &py_list);
	} else if (rec && rec->bins.size == 0) {
		as_list *list = NULL;
		list_to_pyobject(self, &err, list, &py_list);
	}

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}

	EXCEPTION_ON_ERROR();

	return py_list;
}

/**
 *******************************************************************************************************
 * Remove elements from the list which are not within the range starting at the given index plus count.
 *
 * @param self                  AerospikeClient object
 * @param args                  The args is a tuple object containing an argument
 *                              list passed from Python to a C function
 * @param kwds                  Dictionary of keywords
 *
 * Returns an integer status. 0(Zero) is success value.
 * In case of error,appropriate exceptions will be raised.
 *******************************************************************************************************
 */
PyObject * AerospikeClient_ListTrim(AerospikeClient * self, PyObject * args, PyObject * kwds)
{
	BASE_VARIABLES

	uint64_t index;
	uint64_t count;
	as_record *rec = NULL;
	as_operations ops;
	as_operations_inita(&ops, 1);
	// Python Function Keyword Arguments
	static char * kwlist[] = {"key", "bin", "index", "count", "meta", "policy", NULL};

	// Python Function Argument Parsing
	if (PyArg_ParseTupleAndKeywords(args, kwds, "OOll|OO:list_trim", kwlist,
				&py_key, &py_bin, &index, &count, &py_meta, &py_policy) == false) {
		return NULL;
	}

	CHECK_CONNECTED_AND_CDT_SUPPORT();

	POLICY_KEY_META_BIN();

	as_operations_add_list_trim(&ops, bin, index, count);

	DO_OPERATION(&rec);

CLEANUP:
	as_operations_destroy(&ops);
	if (rec) {
		as_record_destroy(rec);
	}
	EXCEPTION_ON_ERROR();

	return PyLong_FromLong(0);
}

