/*
 * Copyright (c) 2001-2018 Mellanox Technologies, Ltd. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#include <vlogger/vlogger.h>
#include <stdio.h>
#include <stdlib.h>
#include "sm.h"
#include "sm_fifo.h"
#include <string.h>

#define MODULE_NAME			"SM_TEST: "

#define NOT_IN_USE(a)                   ((void)(a))

/* SM example */

typedef enum {
	SM_ST_A = 0,
	SM_ST_B,
	SM_ST_C,
	SM_ST_LAST
} sm_state_e;



typedef enum {
	SM_EV_1 = 0,
	SM_EV_2,
	SM_EV_3,
	SM_EV_4,
	SM_EV_LAST
} sm_event_e;

// Debug functions  definitions
const char *state_num_to_str_func(int state);
const char* event_num_to_str_func(int event);
void  print_event_info(int state, int event, void* app_hndl);

void sm_st_entry(const sm_info_t& info);
void sm_st_leave(const sm_info_t& info);
void sm_st_A_ev_1(const sm_info_t& info);
void sm_st_A_ev_2(const sm_info_t& info);
void sm_st_A_ev_3(const sm_info_t& info);
void sm_st_B_ev_1(const sm_info_t& info);
void sm_st_B_ev_2(const sm_info_t& info);
void sm_st_B_ev_3(const sm_info_t& info);
void sm_st_C_ev_1(const sm_info_t& info);
void sm_st_C_ev_2(const sm_info_t& info);
void sm_st_C_ev_3(const sm_info_t& info);

void sm_default_trans_func(const sm_info_t& info);


//// The short table
sm_short_table_line_t sm_short_table[] = {
// 	{curr state,	event, 		next state,	action func  }
	{ SM_ST_A,      SM_STATE_ENTRY, SM_NO_ST,       sm_st_entry}, 
	{ SM_ST_A,      SM_EV_1,        SM_ST_A,        sm_st_A_ev_1}, 
	{ SM_ST_A,      SM_EV_2,        SM_ST_B,sm_st_A_ev_2}, 
	{ SM_ST_A,      SM_EV_3,        SM_ST_C,       sm_st_A_ev_3}, 
	{ SM_ST_A,      SM_STATE_LEAVE, SM_NO_ST,      sm_st_leave}, 

	{ SM_ST_B,      SM_STATE_ENTRY, SM_NO_ST,      sm_st_entry}, 
	{ SM_ST_B,      SM_EV_1,        SM_ST_B,       sm_st_B_ev_1}, 
	{ SM_ST_B,      SM_EV_2,        SM_ST_C,       sm_st_B_ev_2}, 
	{ SM_ST_B,      SM_EV_3,        SM_ST_A,       sm_st_B_ev_3}, 
	{ SM_ST_B,      SM_STATE_LEAVE, SM_NO_ST,      sm_st_leave}, 

	{ SM_ST_C,      SM_STATE_ENTRY, SM_NO_ST,      sm_st_entry}, 
	{ SM_ST_C,      SM_EV_1,        SM_ST_C,       sm_st_C_ev_1}, 
	{ SM_ST_C,      SM_EV_2,        SM_ST_A,       sm_st_C_ev_2}, 
	{ SM_ST_C,      SM_EV_3,        SM_ST_B,       sm_st_C_ev_3},
	{ SM_ST_C,      SM_STATE_LEAVE, SM_NO_ST,      sm_st_leave},

	SM_TABLE_END
};

#if 0

typedef struct {
	int  event;             
	char* name;
} test_entry;

void fifo_test()
{
	sm_fifo my_fifo;
	int i=0;
	fifo_entry_t ret;

	test_entry arr_num[] = {
		{1, "one"},
		{2, "two"},
		{3, "three"},
		{4, "four"},
		{5, "five"},
		{6, "six"},
		{7, "seven"},
		{8, "eight"},
		{9, "nine"},
		{10,"ten"}
	};


	vlog_printf(VLOG_INFO, "fifo test\n");

	while (i<10) {
		my_fifo.push_back(arr_num[i].event, (void *) arr_num[i].name );
		vlog_printf(VLOG_ERROR, "element %d was inserted\n", arr_num[i]);
		my_fifo.debug_print_fifo();
		ret = my_fifo.get_front();
		vlog_printf(VLOG_ERROR, "element %d was removed (%s)\n", ret.event, ret.ev_data);
		my_fifo.debug_print_fifo();
		i++;
	}
	/*while (i>0) {
			ret = my_fifo.get_element();
			vlog_printf(VLOG_ERROR, "element %d was removeded\n", ret);
			my_fifo.debug_print_fifo();
			i--;
	}*/
}

#endif


#if _BullseyeCoverage
    #pragma BullseyeCoverage off
#endif


state_machine* g_sm;

int main(int argc, char *argv[])
{
	vlog_levels_t log_level = VLOG_DETAILS;

	if (argc > 1) {
		log_level = log_level::from_str(argv[1], VLOG_INIT);
		if (log_level == VLOG_INIT ) {
			printf("illegal log level %s\n", argv[1]);
			return -1;
		}
	}
	vlog_start("SM_TEST", log_level, NULL, 0);
	//fifo_test();

	g_sm = new state_machine(NULL,
				 SM_ST_A,
				 SM_ST_LAST, 
				 SM_EV_LAST, 
				 sm_short_table, 
				 sm_default_trans_func, 
				 NULL, 
				 NULL,
				 print_event_info);

	g_sm->process_event(SM_EV_2,(void *)"event 2");

	delete g_sm;
}



//// Debug functions  definitions
const char* state_num_to_str_func(int state)
{
	switch (state) {
	case SM_ST_A:
		return "SM_ST_A";
	case SM_ST_B:
		return "SM_ST_B";
	case SM_ST_C:
		return "SM_ST_C";
	default:
		return "Undefined";
	} 

}

const char* event_num_to_str_func(int event)
{
	switch (event) {
	case SM_EV_1:
		return "SM_EV_1";
	case SM_EV_2:
		return "SM_EV_2";
	case SM_EV_3:
		return "SM_EV_3";
	case SM_EV_4:
		return "SM_EV_4";
	default:
		return "Undefined";
	} 
}

void print_event_info(int state, int event, void* app_hndl)
{
	NOT_IN_USE(app_hndl);
	printf(MODULE_NAME "Got event %s (%d) in state %s (%d)\n", 
	       event_num_to_str_func(event), event, state_num_to_str_func(state), state);
}

////////////////////////////////////////
// SM Entry Function       
void sm_st_entry(const sm_info_t& info)
{
	printf(MODULE_NAME "State changed %s (%d) => %s (%d)\n", 
	       state_num_to_str_func(info.old_state), info.old_state, 
	       state_num_to_str_func(info.new_state), info.new_state);
}

////////////////////////////////////////
// SM Leave Function
void sm_st_leave(const sm_info_t& info)
{
	printf(MODULE_NAME "State changing %s (%d) => %s (%d)\n", 
	       state_num_to_str_func(info.old_state), info.old_state, 
	       state_num_to_str_func(info.new_state), info.new_state);
}

////////////////////////////////////////
// SM Transition Functions                                        
void sm_default_trans_func(const sm_info_t& info)
{
	printf(MODULE_NAME "Default Transition: Handle event %s (%d) in state %s (%d)\n",
	       event_num_to_str_func(info.event), info.event,
	       state_num_to_str_func(info.old_state), info.old_state);
	if (info.new_state != SM_ST_STAY) {
		printf(MODULE_NAME "Default Transition: Moving to state %s (%d)\n", state_num_to_str_func(info.new_state), info.new_state);

	}
}

void sm_st_A_ev_1(const sm_info_t& info)
{
	printf(MODULE_NAME "Got event %s in state A\n", (char*)info.ev_data);
}

void sm_st_A_ev_2(const sm_info_t& info)
{
	printf(MODULE_NAME "Got event %s in state A\n", (char*)info.ev_data);
	g_sm->process_event(SM_EV_4, (void*)"event 4"); 
	g_sm->process_event(SM_EV_1, (void*)"event 1"); 
	g_sm->process_event(SM_EV_2, (void*)"event 2"); 
	g_sm->process_event(SM_EV_3, (void*)"event 3"); 
	g_sm->process_event(SM_EV_4, (void*)"event 4"); 
	//g_sm->m_sm_fifo.debug_print_fifo();
}

void sm_st_A_ev_3(const sm_info_t& info)
{
	printf(MODULE_NAME "Got event %s\n", (char*)info.ev_data);
}

void sm_st_B_ev_1(const sm_info_t& info)
{
	NOT_IN_USE(info);
	printf(MODULE_NAME "Got event %s\n", event_num_to_str_func(SM_EV_1));
}

void sm_st_B_ev_2(const sm_info_t& info)
{
	printf(MODULE_NAME "Got event %s\n", (char*)info.ev_data);
	g_sm->process_event(SM_EV_1, (void*)"event 1");   
}

void sm_st_B_ev_3(const sm_info_t& info)
{
	NOT_IN_USE(info);
	printf(MODULE_NAME "Got event %s\n", event_num_to_str_func(SM_EV_3));
}

void sm_st_C_ev_1(const sm_info_t& info)
{
	NOT_IN_USE(info);
	printf(MODULE_NAME "Got event %s\n", event_num_to_str_func(SM_EV_1));
}

void sm_st_C_ev_2(const sm_info_t& info)
{
	NOT_IN_USE(info);
	printf(MODULE_NAME "Got event %s\n", event_num_to_str_func(SM_EV_2));
	g_sm->process_event(SM_EV_4, (void*)"event 4");   
}

void sm_st_C_ev_3(const sm_info_t& info)
{
	NOT_IN_USE(info);
	printf(MODULE_NAME "Got event %s\n", event_num_to_str_func(SM_EV_3));
}

#if _BullseyeCoverage
    #pragma BullseyeCoverage on
#endif
