#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "taos.h"
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>

void execute_simple_sql(void *taos, char *sql) {
    TAOS_RES *result = taos_query(taos, sql);
    if ( result == NULL || taos_errno(result) != 0) {
        printf( "failed to %s, Reason: %s\n" , sql, taos_errstr(result));
        taos_free_result(result);
        exit(EXIT_FAILURE);
    }
    taos_free_result(result);
}

void taos_stmt_init_test() {
	printf("start taos_stmt_init test \n");
    void *taos = NULL;
	TAOS_STMT *stmt = NULL;
    stmt = taos_stmt_init(taos);
	assert(stmt == NULL);
    // ASM ERROR
	// assert(taos_stmt_close(stmt) != 0);
	taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
	stmt = taos_stmt_init(taos);
	assert(stmt != NULL);
	assert(taos_stmt_close(stmt) == 0);
    printf("finish taos_stmt_init test\n"); 
}
void taos_stmt_preprare_test() {
    printf("start taos_stmt_prepare test\n");
    char *stmt_sql = calloc(1, 1048576);
    TAOS_STMT *stmt = NULL;
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) != 0);
    void *taos = NULL;	
	taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create table super(ts timestamp, c1 int, c2 bigint, c3 float, c4 double, c5 binary(8), c6 smallint, c7 tinyint, c8 bool, c9 nchar(8), c10 timestamp) tags (t1 int, t2 bigint, t3 float, t4 double, t5 binary(8), t6 smallint, t7 tinyint, t8 bool, t9 nchar(8))");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    // below will make client dead lock
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);

    // assert(taos_stmt_close(stmt) == 0);
    // stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    sprintf(stmt_sql, "select from ?");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) != 0);
    assert(taos_stmt_close(stmt) == 0);

    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    sprintf(stmt_sql, "insert into ? values (?,?,?,?,?,?,?,?,?,?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_close(stmt) == 0);

    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    sprintf(stmt_sql, "insert into super values (?,?,?,?,?,?,?,?,?,?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) != 0);
    assert(taos_stmt_close(stmt) == 0);

    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    sprintf(stmt_sql, "insert into ? values (?,?,?,?,?,?,?,?,1,?,?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_close(stmt) == 0);

    free(stmt_sql);
    printf("finish taos_stmt_prepare test\n");
}

void taos_stmt_set_tbname_test() {
    printf("start taos_stmt_set_tbname test\n");
    TAOS_STMT *stmt = NULL;
    char *name = calloc(1, 200);
    // ASM ERROR
    // assert(taos_stmt_set_tbname(stmt, name) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create table super(ts timestamp, c1 int)");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    assert(taos_stmt_set_tbname(stmt, name) != 0);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    sprintf(name, "super");
    assert(stmt != NULL);
    assert(taos_stmt_set_tbname(stmt, name) == 0);
    free(name);
    free(stmt_sql);
    taos_stmt_close(stmt);
    printf("finish taos_stmt_set_tbname test\n");
}

void taos_stmt_set_tbname_tags_test() {
    printf("start taos_stmt_set_tbname_tags test\n");
    TAOS_STMT *stmt = NULL;
    char *name = calloc(1,20);
    TAOS_BIND *tags = calloc(1, sizeof(TAOS_BIND));
    // ASM ERROR
    // assert(taos_stmt_set_tbname_tags(stmt, name, tags) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create stable super(ts timestamp, c1 int) tags (id int)");
    execute_simple_sql(taos, "create table tb using super tags (1)");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? using super tags (?) values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_set_tbname_tags(stmt, name, tags) != 0);
    sprintf(name, "tb");
    assert(taos_stmt_set_tbname_tags(stmt, name, tags) != 0);
    int t = 1;
    tags->buffer_length = TSDB_DATA_TYPE_INT;
    tags->buffer_length = sizeof(uint32_t);
    tags->buffer = &t;
    tags->length = &tags->buffer_length;
    tags->is_null = NULL;
    assert(taos_stmt_set_tbname_tags(stmt, name, tags) == 0);
    free(stmt_sql);
    free(name);
    free(tags);
    taos_stmt_close(stmt);
    printf("finish taos_stmt_set_tbname_tags test\n");
}

void taos_stmt_set_sub_tbname_test() {
    printf("start taos_stmt_set_sub_tbname test\n");
    TAOS_STMT *stmt = NULL;
    char *name = calloc(1, 200);
    // ASM ERROR
    // assert(taos_stmt_set_sub_tbname(stmt, name) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create stable super(ts timestamp, c1 int) tags (id int)");
    execute_simple_sql(taos, "create table tb using super tags (1)");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_set_sub_tbname(stmt, name) != 0);
    sprintf(name, "tb");
    assert(taos_stmt_set_sub_tbname(stmt, name) == 0);
    // assert(taos_load_table_info(taos, "super, tb") == 0);
    // assert(taos_stmt_set_sub_tbname(stmt, name) == 0);
    free(name);
    free(stmt_sql);
    assert(taos_stmt_close(stmt) == 0);
    printf("finish taos_stmt_set_sub_tbname test\n");
}

void taos_stmt_bind_param_test() {
    printf("start taos_stmt_bind_param test\n");
    TAOS_STMT *stmt = NULL;
    TAOS_BIND *binds = NULL;
    assert(taos_stmt_bind_param(stmt, binds) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create table super(ts timestamp, c1 int)");
    stmt = taos_stmt_init(taos);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_bind_param(stmt, binds) != 0);
    free(binds);
    TAOS_BIND *params = calloc(2, sizeof(TAOS_BIND));
    int64_t ts = (int64_t)1591060628000;
    params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
    params[0].buffer_length = sizeof(uint64_t);
    params[0].buffer = &ts;
    params[0].length = &params[0].buffer_length;
    params[0].is_null = NULL;
    int32_t i = (int32_t)21474;
    params[1].buffer_type = TSDB_DATA_TYPE_INT;
    params[1].buffer_length = sizeof(int32_t);
    params[1].buffer = &i;
    params[1].length = &params[1].buffer_length;
    params[1].is_null = NULL;
    assert(taos_stmt_bind_param(stmt, params) != 0);
    assert(taos_stmt_set_tbname(stmt, "super") == 0);
    assert(taos_stmt_bind_param(stmt, params) == 0);
    free(params);
    free(stmt_sql);
    taos_stmt_close(stmt);
    printf("finish taos_stmt_bind_param test\n");
}

void taos_stmt_bind_single_param_batch_test() {
    printf("start taos_stmt_bind_single_param_batch test\n");
    TAOS_STMT *stmt = NULL;
    TAOS_MULTI_BIND *bind = NULL;
    assert(taos_stmt_bind_single_param_batch(stmt, bind, 0) != 0);
    printf("finish taos_stmt_bind_single_param_batch test\n");
}

void taos_stmt_bind_param_batch_test() {
    printf("start taos_stmt_bind_param_batch test\n");
    TAOS_STMT *stmt = NULL;
    TAOS_MULTI_BIND *bind = NULL;
    assert(taos_stmt_bind_param_batch(stmt, bind) != 0);
    printf("finish taos_stmt_bind_param_batch test\n");
}

void taos_stmt_add_batch_test() {
    printf("start taos_stmt_add_batch test\n");
    TAOS_STMT *stmt = NULL;
    assert(taos_stmt_add_batch(stmt) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create table super(ts timestamp, c1 int)");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_add_batch(stmt) != 0);
    TAOS_BIND *params = calloc(2, sizeof(TAOS_BIND));
    int64_t ts = (int64_t)1591060628000;
    params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
    params[0].buffer_length = sizeof(uint64_t);
    params[0].buffer = &ts;
    params[0].length = &params[0].buffer_length;
    params[0].is_null = NULL;
    int32_t i = (int32_t)21474;
    params[1].buffer_type = TSDB_DATA_TYPE_INT;
    params[1].buffer_length = sizeof(int32_t);
    params[1].buffer = &i;
    params[1].length = &params[1].buffer_length;
    params[1].is_null = NULL;
    assert(taos_stmt_set_tbname(stmt, "super") == 0);
    assert(taos_stmt_bind_param(stmt, params) == 0);
    assert(taos_stmt_add_batch(stmt) == 0);
    free(params);
    free(stmt_sql);
    assert(taos_stmt_close(stmt) == 0);
    printf("finish taos_stmt_add_batch test\n");
}

void taos_stmt_execute_test() {
    printf("start taos_stmt_execute test\n");
    TAOS_STMT *stmt = NULL;
    assert(taos_stmt_execute(stmt) != 0);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
	if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    execute_simple_sql(taos, "drop database if exists stmt_test");
    execute_simple_sql(taos, "create database stmt_test");
    execute_simple_sql(taos, "use stmt_test");
    execute_simple_sql(taos, "create table super(ts timestamp, c1 int)");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    assert(taos_stmt_execute(stmt) != 0);
    char* stmt_sql = calloc(1, 1000);
    sprintf(stmt_sql, "insert into ? values (?,?)");
    assert(taos_stmt_prepare(stmt, stmt_sql, 0) == 0);
    assert(taos_stmt_execute(stmt) != 0);
    TAOS_BIND *params = calloc(2, sizeof(TAOS_BIND));
    int64_t ts = (int64_t)1591060628000;
    params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
    params[0].buffer_length = sizeof(uint64_t);
    params[0].buffer = &ts;
    params[0].length = &params[0].buffer_length;
    params[0].is_null = NULL;
    int32_t i = (int32_t)21474;
    params[1].buffer_type = TSDB_DATA_TYPE_INT;
    params[1].buffer_length = sizeof(int32_t);
    params[1].buffer = &i;
    params[1].length = &params[1].buffer_length;
    params[1].is_null = NULL;
    assert(taos_stmt_set_tbname(stmt, "super") == 0);
    assert(taos_stmt_execute(stmt) != 0);
    assert(taos_stmt_bind_param(stmt, params) == 0);
    assert(taos_stmt_execute(stmt) != 0);
    assert(taos_stmt_add_batch(stmt) == 0);
    assert(taos_stmt_execute(stmt) == 0);
    free(params);
    free(stmt_sql);
    assert(taos_stmt_close(stmt) == 0);
    printf("finish taos_stmt_execute test\n");
}

void taos_stmt_use_result_test() {
    printf("start taos_stmt_use_result test\n");
    TAOS_STMT *stmt = NULL;
    assert(taos_stmt_use_result(stmt) == NULL);
    void *taos = taos_connect("127.0.0.1","root","taosdata",NULL,0);
    if(taos == NULL) {
		printf("Cannot connect to tdengine server\n");
		exit(EXIT_FAILURE);
	}
    printf("finish taos_stmt_use_result test\n");
    stmt = taos_stmt_init(taos);
    assert(stmt != NULL);
    TAOS_RES* result = taos_stmt_use_result(stmt);
    int rows = 0;
    TAOS_ROW row;
    while ((row = taos_fetch_row(result))) {
        rows++;
    }
    printf("rows: %d\n", rows);
    taos_free_result(result);
    // assert(taos_stmt_use_result(stmt) == NULL);
    assert(taos_stmt_close(stmt) == 0);
}

void taos_stmt_close_test() {
    printf("start taos_stmt_close test\n");
    // ASM ERROR
    // TAOS_STMT *stmt = NULL;
    // assert(taos_stmt_close(stmt) != 0);
    printf("finish taos_stmt_close test\n");
}

void test_api_reliability() {
	// ASM catch memory leak
    taos_stmt_init_test();
	taos_stmt_preprare_test();
    taos_stmt_set_tbname_test();
    taos_stmt_set_tbname_tags_test();
    taos_stmt_set_sub_tbname_test();
    taos_stmt_bind_param_test();
    taos_stmt_bind_single_param_batch_test();
    taos_stmt_bind_param_batch_test();
    taos_stmt_add_batch_test();
    taos_stmt_execute_test();
    taos_stmt_use_result_test();
    taos_stmt_close_test();
}

int main(int argc, char *argv[]) {
    test_api_reliability();
    return 0;
}