/*******************************************************************************
* Copyright 2013-2021 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/as_exp_operations.h>

#include "client.h"
#include "conversions.h"
#include "exceptions.h"
#include "policy.h"
#include "serializer.h"
#include "expression_operations.h"
#include "cdt_operation_utils.h"

static as_status add_op_expr_read(AerospikeClient *self, as_error *err,
                                  PyObject *op_dict,
                                  as_vector *unicodeStrVector,
                                  as_operations *ops, int serializer_type);

static as_status add_op_expr_write(AerospikeClient *self, as_error *err,
                                   PyObject *op_dict,
                                   as_vector *unicodeStrVector,
                                   as_operations *ops, int serializer_type);

/* End forwards*/
as_status add_new_expr_op(AerospikeClient *self, as_error *err,
                          PyObject *op_dict, as_vector *unicodeStrVector,
                          as_operations *ops, long operation_code,
                          int serializer_type)

{
    switch (operation_code) {

    case OP_EXPR_READ: {
        return add_op_expr_read(self, err, op_dict, unicodeStrVector, ops,
                                serializer_type);
    }

    case OP_EXPR_WRITE: {
        return add_op_expr_write(self, err, op_dict, unicodeStrVector, ops,
                                 serializer_type);
    }

    default:
        // This should never be possible since we only get here if we know that the operation is valid.
        return as_error_update(err, AEROSPIKE_ERR_PARAM,
                               "Unknown expression operation");
    }

    return err->code;
}

static as_status add_op_expr_write(AerospikeClient *self, as_error *err,
                                   PyObject *op_dict,
                                   as_vector *unicodeStrVector,
                                   as_operations *ops, int serializer_type)
{
    as_exp *exp_list_p = NULL;
    PyObject *py_exp_list = NULL;
    int64_t exp_write_flags = AS_EXP_WRITE_DEFAULT;
    char *bin = NULL;

    if (get_bin(err, op_dict, unicodeStrVector, &bin) != AEROSPIKE_OK) {
        return err->code;
    }

    if (get_int64_t(err, AS_EXPR_FLAGS_KEY, op_dict, &exp_write_flags) !=
        AEROSPIKE_OK) {
        return err->code;
    }

    py_exp_list = PyDict_GetItemString(op_dict, AS_EXPR_KEY);

    if (as_exp_new_from_pyobject(self, py_exp_list, &exp_list_p, err, false) !=
        AEROSPIKE_OK) {
        return err->code;
    }

    if (!as_operations_exp_write(ops, bin, exp_list_p, exp_write_flags)) {
        as_error_update(err, AEROSPIKE_ERR_CLIENT,
                        "Failed to pack write expression op.");
    }

    if (exp_list_p) {
        as_exp_destroy(exp_list_p);
    }

    return err->code;
}

static as_status add_op_expr_read(AerospikeClient *self, as_error *err,
                                  PyObject *op_dict,
                                  as_vector *unicodeStrVector,
                                  as_operations *ops, int serializer_type)
{
    as_exp *exp_list_p = NULL;
    PyObject *py_exp_list = NULL;
    int64_t exp_read_flags = AS_EXP_READ_DEFAULT;
    char *bin = NULL;

    if (get_bin(err, op_dict, unicodeStrVector, &bin) != AEROSPIKE_OK) {
        return err->code;
    }

    if (get_int64_t(err, AS_EXPR_FLAGS_KEY, op_dict, &exp_read_flags) !=
        AEROSPIKE_OK) {
        return err->code;
    }

    py_exp_list = PyDict_GetItemString(op_dict, AS_EXPR_KEY);

    if (as_exp_new_from_pyobject(self, py_exp_list, &exp_list_p, err, false) !=
        AEROSPIKE_OK) {
        return err->code;
    }

    if (!as_operations_exp_read(ops, bin, exp_list_p, exp_read_flags)) {
        as_error_update(err, AEROSPIKE_ERR_CLIENT,
                        "Failed to pack read expression op.");
    }

    if (exp_list_p) {
        as_exp_destroy(exp_list_p);
    }

    return err->code;
}
