/*
 * %CopyrightBegin%
 * 
 * Copyright Ericsson AB 2001-2016. All Rights Reserved.
 * 
 * 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.
 * 
 * %CopyrightEnd%
 */

/*
 * Purpose: Tests the functions in ei_connect.c.
 * Author: Bjorn Gustavsson (rewritten somewhat by Jakob Cederlund)
 *
 * See the ei_connect_SUITE.erl file for a "table of contents".
 */

#include <stdio.h>
#include <string.h>
#ifdef VXWORKS
#include "reclaim.h"
#endif

#include "ei_runner.h"

static void cmd_ei_connect_init(char* buf, int len);
static void cmd_ei_connect(char* buf, int len);
static void cmd_ei_send(char* buf, int len);
static void cmd_ei_format_pid(char* buf, int len);
static void cmd_ei_send_funs(char* buf, int len);
static void cmd_ei_reg_send(char* buf, int len);
static void cmd_ei_rpc(char* buf, int len);
static void cmd_ei_set_get_tracelevel(char* buf, int len);

static void send_errno_result(int value);

ei_cnode ec;


static struct {
    char* name;
    int num_args;		/* Number of arguments. */
    void (*func)(char* buf, int len);
} commands[] = {
    "ei_connect_init",       3, cmd_ei_connect_init,
    "ei_connect", 	     1, cmd_ei_connect,
    "ei_send",  	     3, cmd_ei_send,
    "ei_send_funs",  	     3, cmd_ei_send_funs,
    "ei_reg_send", 	     3, cmd_ei_reg_send,
    "ei_rpc",  		     4, cmd_ei_rpc,
    "ei_set_get_tracelevel", 1, cmd_ei_set_get_tracelevel,
    "ei_format_pid",         2, cmd_ei_format_pid,
};


/*
 * Sends a list contaning all data types to the Erlang side.
 */

TESTCASE(interpret)
{
    ei_x_buff x;
    int i;
    ei_term term;

    ei_x_new(&x);
    for (;;) {
	if (get_bin_term(&x, &term)) {
	    report(1);
	    return;
	} else {
	    char* buf = x.buff, func[MAXATOMLEN];
	    int index = x.index, arity;
	    if (term.ei_type != ERL_SMALL_TUPLE_EXT || term.arity != 2)
		fail("term should be a tuple of size 2");
	    if (ei_decode_atom(buf, &index, func) < 0)
		fail("function name should be an atom");
	    if (ei_decode_tuple_header(buf, &index, &arity) != 0)
		fail("function arguments should be a tuple");
	    for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
		if (strcmp(func, commands[i].name) == 0) {
		    if (arity != commands[i].num_args)
			fail("wrong number of arguments");
		    commands[i].func(buf + index, x.buffsz - index);
		    break;
		}
	    }
	    if (i >= sizeof(commands)/sizeof(commands[0])) {
		message("\"%d\" \n", func);
		fail("bad command");
	    }
	}
    }	
}


static void cmd_ei_connect_init(char* buf, int len)
{
    int index = 0, r = 0;
    int type, size;
    long l;
    char b[100];
    char cookie[MAXATOMLEN], * cp = cookie;
    ei_x_buff res;
    if (ei_decode_long(buf, &index, &l) < 0)
	fail("expected int");
    sprintf(b, "c%ld", l);
    /* FIXME don't use internal and maybe use skip?! */
    ei_get_type_internal(buf, &index, &type, &size);
    if (ei_decode_atom(buf, &index, cookie) < 0)
	fail("expected atom (cookie)");
    if (cookie[0] == '\0')
	cp = NULL;
    r = ei_connect_init(&ec, b, cp, 0);
    ei_x_new_with_version(&res);
    ei_x_encode_long(&res, r);
    send_bin_term(&res);
    ei_x_free(&res);
}

static void cmd_ei_connect(char* buf, int len)
{
    int index = 0;
    char node[256];
    int i;
    if (ei_decode_atom(buf, &index, node) < 0)
	fail("expected atom");
    i=ei_connect(&ec, node);
#ifdef VXWORKS
    if(i >= 0) {
	save_fd(i);
    }
#endif
    send_errno_result(i);
}

static void cmd_ei_set_get_tracelevel(char* buf, int len)
{
    int  index = 0;
    long level = 0;
    long ret   = 0;
    ei_x_buff x;

    if (ei_decode_long(buf, &index, &level) < 0) {
	fail("expected long");
    }
    
    ei_set_tracelevel((int)level);

    ret = (long) ei_get_tracelevel();

    ei_x_new_with_version(&x);
    ei_x_encode_tuple_header(&x, 2);
    ei_x_encode_atom(&x, "tracelevel");
    ei_x_encode_long(&x, ret);
    send_bin_term(&x);
    ei_x_free(&x);
}

static void cmd_ei_send(char* buf, int len)
{
    int index = 0;
    long fd;
    erlang_pid pid;
    ei_x_buff x;

    if (ei_decode_long(buf, &index, &fd) < 0)
	fail("expected long");
    if (ei_decode_pid(buf, &index, &pid) < 0)
	fail("expected pid (node)");
    if (ei_x_new_with_version(&x) < 0)
	fail("ei_x_new_with_version");
    if (ei_x_append_buf(&x, &buf[index], len - index) < 0)
	fail("append");
    send_errno_result(ei_send(fd, &pid, x.buff, x.index));
    ei_x_free(&x);
}

static void cmd_ei_format_pid(char* buf, int len)
{
    int index = 0;
    long fd;
    erlang_pid pid;
    ei_x_buff x;

    if (ei_decode_long(buf, &index, &fd) < 0)
	fail("expected long");
    if (ei_decode_pid(buf, &index, &pid) < 0)
	fail("expected pid (node)");
    if (ei_x_new_with_version(&x) < 0)
	fail("ei_x_new_with_version");
    if (ei_x_format_wo_ver(&x, "~p", &pid) < 0)
	fail("ei_x_format_wo_ver");
    send_errno_result(ei_send(fd, &pid, x.buff, x.index));
    ei_x_free(&x);
}

static void cmd_ei_send_funs(char* buf, int len)
{
    int index = 0, n;
    long fd;
    erlang_pid pid;
    ei_x_buff x;
    erlang_fun fun1, fun2;

    if (ei_decode_long(buf, &index, &fd) < 0)
	fail("expected long");
    if (ei_decode_pid(buf, &index, &pid) < 0)
	fail("expected pid (node)");
    if (ei_decode_tuple_header(buf, &index, &n) < 0)
	fail("expected tuple");
    if (n != 2)
	fail("expected tuple");
    if (ei_decode_fun(buf, &index, &fun1) < 0)
	fail("expected Fun1");
    if (ei_decode_fun(buf, &index, &fun2) < 0)
	fail("expected Fun2");
    if (ei_x_new_with_version(&x) < 0)
	fail("ei_x_new_with_version");
    if (ei_x_encode_tuple_header(&x, 2) < 0)
	fail("encode tuple header");
    if (ei_x_encode_fun(&x, &fun1) < 0)
	fail("encode fun1");
    if (ei_x_encode_fun(&x, &fun2) < 0)
	fail("encode fun2");
    free_fun(&fun1);
    free_fun(&fun2);
    send_errno_result(ei_send(fd, &pid, x.buff, x.index));
    ei_x_free(&x);
}

static void cmd_ei_reg_send(char* buf, int len)
{
    int index = 0;
    long fd;
    char reg_name[MAXATOMLEN];
    erlang_pid pid;
    ei_x_buff x;
    
    if (ei_decode_long(buf, &index, &fd) < 0)
	fail("expected long (fd)");
    if (ei_decode_atom(buf, &index, reg_name) < 0)
	fail("expected atom (reg name)");
    if (ei_x_new_with_version(&x) < 0)
	fail("ei_x_new_with_version");
    if (ei_x_append_buf(&x, &buf[index], len - index) < 0)
	fail("append");
    send_errno_result(ei_reg_send(&ec, fd,
				  reg_name, x.buff, x.index));
    ei_x_free(&x);
}

static void cmd_ei_rpc(char* buf, int len)
{
    int index = 0, n;
    long fd;
    erlang_pid pid;
    ei_x_buff x, rpc_x;
    int r;
    char mod[MAXATOMLEN], func[MAXATOMLEN];

#if 0 && defined(__WIN32__) 
    DebugBreak();
#endif

    if (ei_decode_long(buf, &index, &fd) < 0)
	fail("expected long");
    if (ei_decode_pid(buf, &index, &pid) < 0)
	fail("expected pid (node)");
    if (ei_decode_tuple_header(buf, &index, &n) < 0 && n < 2)
	fail("expected tuple {module, function}");
    if (ei_decode_atom(buf, &index, mod) < 0)
	fail("expected atom (module)");
    if (ei_decode_atom(buf, &index, func) < 0)
	fail("expected atom (function)");
    message("pid %s %d %d %d\n", pid.node, pid.num, pid.serial, pid.creation);
    message("{%s, %s}\n", mod, func);
    if (ei_x_new(&rpc_x) < 0)
	fail("ei_x_new");
    if (ei_rpc(&ec, fd, mod, func, &buf[index], len - index, &rpc_x) < 0)
	fail("ei_rpc");
    if (ei_x_new_with_version(&x) < 0)
 	fail("ei_x_new_with_version");
    if (ei_x_append(&x, &rpc_x) < 0)
	fail("append");
    send_bin_term(&x);
    /*send_errno_result(ei_send(&ec, fd, &pid, x.buff, x.index));*/
    ei_x_free(&x);
    ei_x_free(&rpc_x);
}

static void send_errno_result(int value)
{
    ei_x_buff x;
    ei_x_new_with_version(&x);
    ei_x_encode_tuple_header(&x, 2);
    ei_x_encode_long(&x, value);
    ei_x_encode_long(&x, erl_errno);
    send_bin_term(&x);
    ei_x_free(&x);
}
