#include "connect.h"
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <asm/fcntl.h>
#include <asm/ioctl.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <avm/sammel/simple_proc.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/file.h>
#include <linux/jiffies.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/kthread.h>
#include <asm/errno.h>
#if defined(CONFIG_AVM_EVENT) || defined(CONFIG_AVM_EVENT_MODULE)
#define AVM_LED_INTERNAL
#include <avm/event/event.h>
#undef AVM_LED_INTERNAL
#endif

#include "../hui_internal.h"

extern void *button_event_handle;

enum access_type access = access_type_plc_bridge;
enum wlan_uplink uplink = wlan_uplink_down;
unsigned int log_level = 0;
static int boot_complete;
int wlan_on = 0;

//TODO Save async states more beautiful
void store_async_state(int async_event, int val){
    if(async_event == event_enable_debug){
        log_level = val;
        return;
    }
    if(!val){
        return;
    }

    switch(async_event){
        case event_access_type_plc_bridge:
            CONNECT_LOG("[avm_connect]: PLC BRIDGE\n");
            access = access_type_plc_bridge;
            connect_log_access_type(access);
            break;
        case event_access_type_wlan_bridge:
            CONNECT_LOG("[avm_connect]: WLAN BRIDGE\n");
            access = access_type_wlan_bridge;
            connect_log_access_type(access);
            break;
        case event_access_type_lan_bridge:
            CONNECT_LOG("[avm_connect]: LAN BRIDGE\n");
            access = access_type_lan_bridge;
            connect_log_access_type(access);
            break;
        case event_wlan_sta_register:
            CONNECT_LOG("[avm_connect]: WLAN UPLINK UP\n");
            uplink = wlan_uplink_up;
            connect_log_uplink(uplink);
            break;
        case event_wlan_sta_unregister:
        case event_wlan_sta_stopping:
            CONNECT_LOG("[avm_connect]: WLAN UPLINK DOWN\n");
            uplink = wlan_uplink_down;
            connect_log_uplink(uplink);
            break;
        case event_wlan_on:
            wlan_on = 1;
            boot_complete = 1;
            break;
        case event_wlan_error:
        case event_wlan_off:
            wlan_on = 0;
            boot_complete = 1;
            break;
        default:
            break;
    }
}

/* LED Events to State Machine Events */
int led_event_to_state_event(enum _led_event event) {
	switch (event) {
	case event_wlan_sta_wps_no_connect_start: return connect_event_wlan_sta_wps_no_connect_start;
	case event_wlan_sta_wps_no_connect_error: return connect_event_wlan_sta_wps_no_connect_error;
	case event_wlan_sta_wps_no_connect_timeout: return connect_event_wlan_sta_wps_no_connect_timeout;
	case event_wlan_sta_wps_no_connect_success: return connect_event_wlan_sta_wps_no_connect_success;
	case event_wlan_sta_wps_no_connect_done: return connect_event_wlan_sta_wps_no_connect_done;
	case event_nexus_pairing_start: return connect_event_nexus_pairing_start;
	case event_nexus_pairing_error: return connect_event_nexus_pairing_error;
	case event_nexus_pairing_success: return connect_event_nexus_pairing_success;
	case event_nexus_pairing_done: return connect_event_nexus_pairing_done;
	case event_nexus_pairing_timeout: return connect_event_nexus_pairing_timeout;
	case event_nexus_pairing_no_trustee_pending: return connect_event_nexus_pairing_no_trustee_pending;
	case event_plc_pairing_start: return connect_event_plc_pairing_start;
	case event_plc_pairing_stop: return connect_event_plc_pairing_stop;
	case event_plc_pairing_done: return connect_event_plc_pairing_done;
	case event_plc_pairing_error: return connect_event_plc_pairing_error;
	case event_plc_pairing_timeout: return connect_event_plc_pairing_timeout;
	case event_plc_pairing_success: return connect_event_plc_pairing_success;
	case event_plc_pairing_external_error: return connect_event_plc_pairing_external_error;
	case event_wps_start: return connect_event_wps_start;
	case event_wps_error: return connect_event_wps_error;
	case event_wps_timeout: return connect_event_wps_timeout;
	case event_wps_success: return connect_event_wps_success;
	case event_wps_done: return connect_event_wps_done;
	case event_wlan_sta_wps_start: return connect_event_wlan_sta_wps_start;
	case event_wlan_sta_wps_error: return connect_event_wlan_sta_wps_error;
	case event_wlan_sta_wps_timeout: return connect_event_wlan_sta_wps_timeout;
	case event_wlan_sta_wps_success: return connect_event_wlan_sta_wps_success;
	case event_wlan_sta_wps_done: return connect_event_wlan_sta_wps_done;
	case event_dect_stick_and_surf_start: return connect_event_dect_stick_and_surf_start;
	case event_dect_stick_and_surf_error: return connect_event_dect_stick_and_surf_error;
	case event_dect_stick_and_surf_success: return connect_event_dect_stick_and_surf_success;
	case event_dect_stick_and_surf_done: return connect_event_dect_stick_and_surf_done;
	case event_dect_stick_and_surf_timeout: return connect_event_dect_stick_and_surf_timeout;
	case event_lan_pairing_start: return connect_event_lan_pairing_start;
	case event_lan_pairing_error: return connect_event_lan_pairing_error;
	case event_lan_pairing_timeout: return connect_event_lan_pairing_timeout;
	case event_lan_pairing_success: return connect_event_lan_pairing_success;
	case event_lan_pairing_done: return connect_event_lan_pairing_done;
	case event_wlan_on: return connect_event_boot_complete;
	case event_wlan_error: return connect_event_boot_complete;
	case event_wlan_off: return connect_event_boot_complete;
	case event_button_events_disable: return connect_event_button_disable;
	default: return -1;
	}
}

int button_event_to_state_event(int event) {
	switch (event) {
	case avm_event_push_button_connect_methode1: return connect_event_methode1_start;
	case avm_event_push_button_connect_methode2: return connect_event_methode2_start;
	case avm_event_push_button_connect_methode3: return connect_event_methode3_start;
	case avm_event_push_button_connect_methode4: return connect_event_methode4_start;
	case avm_event_push_button_connect_methode5: return connect_event_methode5_start;
	case avm_event_push_button_connect_methode6: return connect_event_methode6_start;
	default: return -1;
	}
}

const char* hui_button_event_to_name(int button) {
	switch (button) {
		case avm_event_push_button_connect_methode1: return "push_button_connect_methode1";
		case avm_event_push_button_connect_methode2: return "push_button_connect_methode2";
		case avm_event_push_button_connect_methode3: return "push_button_connect_methode3";
		case avm_event_push_button_connect_methode4: return "push_button_connect_methode4";
		case avm_event_push_button_connect_methode5: return "push_button_connect_methode5";
		case avm_event_push_button_connect_methode6: return "push_button_connect_methode6";
		case avm_event_push_button_wlan_wps: return "push_button_wlan_wps";
		case avm_event_push_button_wlan_wps_off: return "push_button_wlan_wps_off";
		case avm_event_push_button_wlan_wps_station_off: return "avm_event_push_button_wlan_wps_station_off";
		case avm_event_push_button_wlan_wps_station: return "avm_event_push_button_wlan_wps_station";
		case avm_event_push_button_nexus_pairing: return "push_button_nexus_pairing";
		case avm_event_push_button_nexus_pairing_off: return "push_button_nexus_pairing_off";
		case avm_event_push_button_dect_pairing: return "push_button_dect_pairing";
		case avm_event_push_button_dect_pairing_off: return "push_button_dect_pairing_off";
		case avm_event_push_button_plc_pairing: return "push_button_plc_pairing";
		case avm_event_push_button_plc_pairing_off: return "push_button_plc_pairing_off";
		case avm_event_push_button_wlan_sta_wps_no_connect: return "push_button_wlan_sta_wps_no_connect";
		case avm_event_push_button_wlan_sta_wps_no_connect_off: return "push_button_wlan_sta_wps_no_connect_off";
		case avm_event_push_button_connect_off: return "push_button_connect_off";
		case avm_event_push_button_connect_timeout: return "push_button_connect_timeout";
		case avm_event_push_button_connect_error: return "push_button_connect_error";
		case avm_event_push_button_connect_success: return "push_button_connect_success";
		case avm_event_push_button_connect_started: return "push_button_connect_started";
		case avm_event_push_button_lan_pairing: return "push_button_lan_pairing";
		case avm_event_push_button_lan_pairing_off: return "push_button_lan_pairing_off";
		default: return NULL;
	}
};
/* selects currently active state machine (methode 1...6) */
struct connect_state * state_machine_ptr;
/* points to currect state */
struct connect_state * state_machine_current_state;
enum connect_state_label state_machine_current_state_label;
enum connect_event delayed_event = unused;


void send_button_event(enum _avm_event_push_button_key push_button_id){
	CONNECT_DEBUG(3, "[avm_connect] sending <%s(%d)>\n",
		hui_button_event_to_name(push_button_id), push_button_id);
	avm_hui_send_button_event(push_button_id, 1);
}

void send_led_event(enum _led_event event, unsigned int value){
	avm_hui_send_event(event, value);
}

/* State Definitions */
void state_machine_set_state_machine(enum connect_event new_state){
    switch(new_state){
        case connect_event_methode1_start:
            send_led_event(event_connect_methode, 1);
            state_machine_ptr = state_machine_methode1;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode1\n", __func__);
            break;
        case connect_event_methode2_start:
            send_led_event(event_connect_methode, 2);
            state_machine_ptr = state_machine_methode2;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode2\n", __func__);
            break;
        case connect_event_methode3_start:
            send_led_event(event_connect_methode, 3);
            state_machine_ptr = state_machine_methode3;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode3\n", __func__);
            break;
        case connect_event_methode4_start:
            send_led_event(event_connect_methode, 4);
            state_machine_ptr = state_machine_methode4;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode4\n", __func__);
            break;
        case connect_event_methode5_start:
            send_led_event(event_connect_methode, 5);
            state_machine_ptr = state_machine_methode5;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode5\n", __func__);
            break;
        case connect_event_methode6_start:
            send_led_event(event_connect_methode, 6);
            state_machine_ptr = state_machine_methode6;
            CONNECT_DEBUG(3, "[avm_connect][%s] activating methode6\n", __func__);
            break;
        default:
            break;
    }
}
/* Idle State Machine */
struct connect_state state_inactive = {
    .description = "idle",
    .post_func = state_machine_set_state_machine,
    .transitions = {
        {.event = connect_event_methode1_start, .next_state = connect_state_wait},
        {.event = connect_event_methode2_start, .next_state = connect_state_wait},
        {.event = connect_event_methode3_start, .next_state = connect_state_wait},
        {.event = connect_event_methode4_start, .next_state = connect_state_wait},
        {.event = connect_event_methode5_start, .next_state = connect_state_wait},
        {.event = connect_event_methode6_start, .next_state = connect_state_wait},
    }
};

/* State Functions */
void connect_state_wait_pre_func(enum connect_event new_state)
{
    // We've already booted, so just trigger away
    if (boot_complete)
        state_machine_queue_trigger_event(connect_event_boot_complete);
}


/* State Machine */


void state_machine_trigger_event(enum connect_event trigger){
    int i,k;
    /* State Machine Ptr must be set */
    if(state_machine_ptr){
        /* Current State Ptr must be set */
        if(state_machine_current_state){
            /* Loop over all possible state transitions */
            for(i = 0; i < MAX_STATE_TRANSITIONS; i++){
                /* Did we find a match ? */
                if(state_machine_current_state->transitions[i].event == trigger){
                    /* We found a match, delete possibly started timeouts */
                    state_machine_delete_delayed_trigger();
                    /* is the post func of the old state set? if yes execute */
                    if(state_machine_current_state->post_func)
                        state_machine_current_state->post_func(trigger);
                    /* Does the new state have a post led event set? if yes send */
                    if(state_machine_current_state->post_led_event)
                        send_led_event(state_machine_current_state->post_led_event, 1);
                    /* Send all post button events */
                    for(k = 0; k < MAX_STATE_TRANSITION_EVENTS; k++){
                        if(state_machine_current_state->post_button_events[k])
                            send_button_event(state_machine_current_state->post_button_events[k]);
                    }
                    if(state_machine_current_state->description && state_machine_ptr[state_machine_current_state->transitions[i].next_state].description){
                        CONNECT_LOG("[avm_connect] (%s): --> %s\n", state_machine_current_state->description, state_machine_ptr[state_machine_current_state->transitions[i].next_state].description);
                        connect_log_new_state(state_machine_ptr[state_machine_current_state->transitions[i].next_state].description);
                    }
                    /* Set new state */
                    state_machine_current_state = &state_machine_ptr[state_machine_current_state->transitions[i].next_state];
                    /* Set new state Label*/
                    state_machine_current_state_label = state_machine_current_state->transitions[i].next_state;
                    /* Does the new state have a pre led event set? if yes send */
                    if(state_machine_current_state->pre_led_event)
                        send_led_event(state_machine_current_state->pre_led_event, 1);
                    /* Send all pre button events */
                    for(k = 0; k < MAX_STATE_TRANSITION_EVENTS; k++){
                        if(state_machine_current_state->pre_button_events[k])
                            send_button_event(state_machine_current_state->pre_button_events[k]);
                    }
                    /* Does the new state have a pre_func set? if yes execute */
                    if(state_machine_current_state->pre_func)
                        state_machine_current_state->pre_func(state_machine_current_state_label);

                    /* Is an a auto_trigger_event set ?*/
                    if(state_machine_current_state->auto_trigger_event){
                        if(state_machine_current_state->timeout_auto_trigger_ms){
                            state_machine_trigger_event_delayed(state_machine_current_state->timeout_auto_trigger_ms, state_machine_current_state->auto_trigger_event);
                        }
                        else{
                            state_machine_queue_trigger_event(state_machine_current_state->auto_trigger_event);
                        }
                    }
                    /* if new state does not have any transitions set, we reached final state */
                    /* execute post if set and reinit state_machine */
                    if(state_machine_current_state->transitions[0].event == unused){
                        if(state_machine_current_state->post_func)
                            state_machine_current_state->post_func(trigger);
                        /* Does the new state have a post led event set? if yes send */
                        if(state_machine_current_state->post_led_event)
                            send_led_event(state_machine_current_state->post_led_event, 1);
                        /* Send all post button events */
                        for(k = 0; k < MAX_STATE_TRANSITION_EVENTS; k++){
                            if(state_machine_current_state->post_button_events[k])
                                send_button_event(state_machine_current_state->post_button_events[k]);
                        }
                        if(state_machine_current_state->description && state_inactive.description){
                            CONNECT_LOG("[avm_connect] (%s): --> %s\n", state_machine_current_state->description, state_inactive.description);
                            connect_log_new_state(state_inactive.description);
                        }
                        send_button_event(avm_event_push_button_connect_off);
                        state_machine_ptr = &state_inactive;
                        state_machine_current_state = &state_inactive;
                        state_machine_current_state_label = connect_state_inactive;
                    }
                    return;
                }
            }
        }
        else{
            printk(KERN_ERR"[avm_connect][%s] current state ptr is not set\n", __func__);
        }
    }
    else{
        printk(KERN_ERR"[avm_connect][%s] state ptr is not set\n", __func__);
    }
}

static struct task_struct   *event_thread;
static spinlock_t      event_list_lock;
struct list_head event_list;
static struct timer_list timer_delayed_event;

int event_worker_thread(void *unused __maybe_unused){
    unsigned long flags = 0;
    while(!kthread_should_stop()){
        /* wait for new cmd */
        spin_lock_irqsave(&event_list_lock, flags);
        if(list_empty(&event_list)){
            set_current_state(TASK_INTERRUPTIBLE);
            spin_unlock_irqrestore(&event_list_lock, flags);
            schedule();
        }
        else{
            spin_unlock_irqrestore(&event_list_lock, flags);
        }

        if(!list_empty(&event_list)){
            enum connect_event trigger;
            struct event_list_item * trigger_item = list_first_entry(&event_list, struct event_list_item, list);
            trigger = trigger_item->trigger;
            spin_lock_irqsave(&event_list_lock, flags);
            list_del(&trigger_item->list);
            spin_unlock_irqrestore(&event_list_lock, flags);
            kfree(trigger_item);
            state_machine_trigger_event(trigger);
        }
        else{
            printk(KERN_ERR"[avm_connect][%s] worker thread is running even though list is empty\n", __func__);
        }
    }
    printk(KERN_ERR"[avm_connect][%s]exiting\n", __func__);
    return 0;
}

/* Trigger State Event */
void state_machine_queue_trigger_event(enum connect_event trigger){
    unsigned long flags = 0;
    struct event_list_item * trigger_item;
    trigger_item = kmalloc(sizeof(struct event_list_item), GFP_ATOMIC);
    if(!trigger_item){
        printk(KERN_ERR"[avm_connect][%s] alloc of event_list_item failed\n", __func__);
        return;
    }
    trigger_item->trigger = trigger;
    /* append to list */
    spin_lock_irqsave(&event_list_lock, flags);
    list_add_tail(&trigger_item->list, &event_list);
    spin_unlock_irqrestore(&event_list_lock, flags);
    /* kick thread */
    wake_up_process(event_thread);
}

void delayed_trigger_callback(timer_callback data)
{
    if (delayed_event) {
        state_machine_queue_trigger_event(delayed_event);
    }
}
void state_machine_delete_delayed_trigger(void){
    del_timer_sync(&timer_delayed_event);

}
void state_machine_trigger_event_delayed(int delay_ms, enum connect_event trigger){
    /*--- CONNECT_DEBUG(2, "[avm_connect][%s] event %d delay %d\n", __func__, trigger, delay_ms); ---*/
    del_timer_sync(&timer_delayed_event);
    timer_delayed_event.function = delayed_trigger_callback;
    timer_delayed_event.expires = jiffies + msecs_to_jiffies(delay_ms);
    delayed_event = trigger;
    add_timer(&timer_delayed_event);
}

/* Init */

void state_machine_init(void){
    state_machine_ptr = &state_inactive;
    state_machine_current_state = &state_inactive;
    state_machine_current_state_label = connect_state_inactive;
    INIT_LIST_HEAD(&event_list);
    spin_lock_init(&event_list_lock);
    event_thread = kthread_create(event_worker_thread, NULL, "avm_connect_event");
    HUI_INIT_TIMER(&timer_delayed_event);
    if(event_thread){
        printk(KERN_INFO"[avm_connect][%s] starting event worker thread\n", __func__);
        wake_up_process(event_thread);
    }
    else{
        printk(KERN_ERR"[avm_connect][%s] event worker thread not created\n", __func__);
    }
}

/*  Event Registration */

static void * connect_led_event_handle;
static void * connect_button_event_handle;

static void connect_led_sink_handle(void *context __attribute__((unused)), unsigned char *buf, unsigned int len) {
    struct _avm_event_led_status event;
	int state_event;
    if(len < sizeof(event)) {
        printk(KERN_ERR"[avm_connect][%s] incompatible event len=%u sizeof=%u\n", __func__, len, sizeof(struct _avm_event_led_status));
        return;
    }
    memcpy(&event, buf, sizeof(event));
    if(event.header.id != avm_event_id_led_status) {
        printk(KERN_ERR"[avm_connect][%s] incompatible event (id=%u)\n", __func__, event.header.id);
        return;
    }

	state_event = led_event_to_state_event(event.led);
    if(state_event != -1 && (event.state != 0)){
		CONNECT_LOG("[avm_connect] (%s): received <%s> state %d\n", state_machine_current_state->description,
		avm_hui_get_event_name(event.led), event.state);
		connect_log_led_event(event.led, event.state);
		state_machine_queue_trigger_event(state_event);
    }
    store_async_state(event.led, event.state);
}
static void connect_button_sink_handle(void *context __attribute__((unused)), unsigned char *buf, unsigned int len) {
    struct _avm_event_push_button event;
	int state_event;
    if(len < sizeof(event)) {
        printk(KERN_ERR"[avm_connect][%s] incompatible event len=%u sizeof=%u\n", __func__, len, sizeof(struct _avm_event_push_button));
        return;
    }
    memcpy(&event, buf, sizeof(event));

	state_event = button_event_to_state_event(event.key);
	if(state_event != -1){
		CONNECT_LOG("[avm_connect] (%s): received <%s(%d)> with pressed %d\n", state_machine_current_state->description, hui_button_event_to_name(event.key), event.key, event.pressed);
		connect_log_button_event(event.key, event.pressed);
		if(event.pressed > 0){
			state_machine_queue_trigger_event(state_event);
		}
		/* When pressed == 0 send start events without starting the state machine -> LED Blinking */
		else if(event.key == avm_event_push_button_connect_methode1){
			send_led_event(event_connect_methode, 1);
			send_led_event(event_connect_pairing_start, 1);
		}
		else if(event.key == avm_event_push_button_connect_methode2){
			send_led_event(event_connect_methode, 2);
			send_led_event(event_connect_pairing_start, 1);
		}
		else if(event.key == avm_event_push_button_connect_methode3){
			send_led_event(event_connect_methode, 3);
			send_led_event(event_connect_pairing_start, 1);
		}
		else if(event.key == avm_event_push_button_connect_methode4){
			send_led_event(event_connect_methode, 4);
			send_led_event(event_connect_pairing_start, 1);
		}
		else if(event.key == avm_event_push_button_connect_methode5){
			send_led_event(event_connect_methode, 5);
			send_led_event(event_connect_pairing_start, 1);
		}
		else if(event.key == avm_event_push_button_connect_methode6){
			send_led_event(event_connect_methode, 6);
			send_led_event(event_connect_pairing_start, 1);
		}
	}
}


struct connect_log connect_log_buf[CONNECT_MAX_LOG_ENTRIES];
uint32_t connect_log_buf_pos = 0;
uint8_t connect_log_buf_overflow = 0;
void connect_buf_inc(void){
    connect_log_buf_pos++;
    if(connect_log_buf_pos >= CONNECT_MAX_LOG_ENTRIES){
        connect_log_buf_pos = 0;
        connect_log_buf_overflow = 1;
    }
}

void connect_log_led_event(enum _led_event led_event, uint32_t state){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_led_event;
    connect_log_buf[connect_log_buf_pos].event = led_event;
    connect_log_buf[connect_log_buf_pos].param = state;
    connect_buf_inc();
}
void connect_log_button_event(enum _avm_event_push_button_key push_button_id, uint32_t pressed){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_button_event;
    connect_log_buf[connect_log_buf_pos].event = push_button_id;
    connect_log_buf[connect_log_buf_pos].param = pressed;
    connect_buf_inc();
}
void connect_log_connect_event(enum connect_event event){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_connect_event;
    connect_log_buf[connect_log_buf_pos].event = event;
    connect_buf_inc();
}
void connect_log_new_state(char * new_state_desc){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_new_state;
    connect_log_buf[connect_log_buf_pos].new_state_desc = new_state_desc;
    connect_buf_inc();
}
void connect_log_access_type(enum access_type acc){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_access;
    connect_log_buf[connect_log_buf_pos].event = acc;
    connect_buf_inc();
}
void connect_log_uplink(enum wlan_uplink up){
    struct timeval now;
    do_gettimeofday(&now);
    connect_log_buf[connect_log_buf_pos].totalsecs = now.tv_sec;
    connect_log_buf[connect_log_buf_pos].current_state_desc = state_machine_current_state->description;
    connect_log_buf[connect_log_buf_pos].log_type = log_type_uplink;
    connect_log_buf[connect_log_buf_pos].event = up;
    connect_buf_inc();
}



static void proc_print_header(struct seq_file *seq, uint32_t index){
    struct tm tm_val;
    time_to_tm(connect_log_buf[index].totalsecs, 0, &tm_val);

    seq_printf(seq, "[%lu-%02d-%02d %02d:%02d:%02d]", tm_val.tm_year + 1900, tm_val.tm_mon + 1,
                                                       tm_val.tm_mday,
                                                       tm_val.tm_hour, tm_val.tm_min, tm_val.tm_sec);
    seq_printf(seq, "(%s): ",connect_log_buf[index].current_state_desc );
}
static void proc_print_entry(struct seq_file *seq, uint32_t index){
    proc_print_header(seq, index);
    switch(connect_log_buf[index].log_type){
        case log_type_led_event:
            seq_printf(seq, "received <%s> state %d\n", avm_hui_get_event_name(connect_log_buf[index].event), connect_log_buf[index].param);
            break;
        case log_type_button_event:
            seq_printf(seq, "received <%s(%d)> with pressed %d\n", hui_button_event_to_name(connect_log_buf[index].event), connect_log_buf[index].event, connect_log_buf[index].param);
            break;
        case log_type_connect_event:
            seq_printf(seq, "event %d\n", connect_log_buf[index].event);
            break;
        case log_type_new_state:
            seq_printf(seq, "--> %s\n", connect_log_buf[index].new_state_desc);
            break;
        case log_type_access:
            if(connect_log_buf[index].event == access_type_lan_bridge){
                seq_printf(seq, "LAN BRIDGE\n");
            }
            else if(connect_log_buf[index].event == access_type_plc_bridge){
                seq_printf(seq, "PLC BRIDGE\n");
            }
            else{
                seq_printf(seq, "WLAN BRIDGE\n");
            }
            break;
        case log_type_uplink:
            if(connect_log_buf[index].event == wlan_uplink_up){
                seq_printf(seq, "WLAN UPLINK UP\n");
            }
            else{
                seq_printf(seq, "WLAN UPLINK DOWN\n");
            }
            break;
        default:

            break;
    }
}
static void proc_connect_log(struct seq_file *seq, void *priv __maybe_unused){
    if((connect_log_buf_pos == 0) && (connect_log_buf_overflow == 0)){
        seq_printf(seq, "No Entries\n");
        return;
    }
    else{
        uint32_t i;
        if(connect_log_buf_overflow){
            for(i = connect_log_buf_pos; i < CONNECT_MAX_LOG_ENTRIES; i++){
                proc_print_entry(seq, i);
            }
        }
        for(i = 0; i < connect_log_buf_pos; i++){
            proc_print_entry(seq, i);
        }
    }
}

int connect_init(void){
    struct _avm_event_id_mask id_mask;
    state_machine_init();
    connect_led_event_handle = avm_event_sink_register("avm_led_event_notify",
                                                         avm_event_build_id_mask(&id_mask, 1, avm_event_id_led_status),
                                                         connect_led_sink_handle,
                                                         NULL
                                                        );
    if(!connect_led_event_handle){
        printk(KERN_ERR"[avm_connect][%s] Failed to register sink avm_led_event_notify. Connect System will not work\n", __func__);
        return -ENODEV;
    }
    connect_button_event_handle = avm_event_sink_register("avm_led_event_push_button_notify",
                                                         avm_event_build_id_mask(&id_mask, 1, avm_event_id_push_button),
                                                         connect_button_sink_handle,
                                                         NULL
                                                        );
    if(!connect_button_event_handle){
        printk(KERN_ERR"[avm_connect][%s] Failed to register sink avm_event_id_push_button. Connect System will not work\n", __func__);
        return -ENODEV;
    }

	add_simple_proc_file( "avm/connect_log", NULL, proc_connect_log, NULL);
    return 0;
}

int connect_exit(void){

    if(connect_led_event_handle){
        avm_event_sink_release(connect_led_event_handle);
        connect_led_event_handle = NULL;
    }
    if(connect_button_event_handle){
        avm_event_sink_release(connect_button_event_handle);
        connect_button_event_handle = NULL;
    }
    if(event_thread){
        kthread_stop(event_thread);
    }
    remove_simple_proc_file("avm/connect_log");
    return 0;
}