#include "mock.h"

#include <stdint.h>
#include <string.h>

#include "ble/att_db.h"
#include "hci.h"
#include "hci_dump.h"
#include "l2cap.h"
#include "rijndael.h"


static btstack_linked_list_t event_packet_handlers;

static uint8_t  packet_buffer[256];
static uint16_t packet_buffer_len = 0;

static uint8_t aes128_cyphertext[16];

static int report_aes128;
static int report_random;

static uint32_t lfsr_random;
// #define ENABLE_PACKET_LOGGER
// #define ENABLE_AES128_LOGGER

/* taps: 32 31 29 1; characteristic polynomial: x^32 + x^31 + x^29 + x + 1 */
#define LFSR(a) ((a >> 1) ^ (uint32_t)((0 - (a & 1u)) & 0xd0000001u))

void mock_init(void){
    lfsr_random = 0x12345678;
}

uint8_t * mock_packet_buffer(void){
	return packet_buffer;
}

void mock_clear_packet_buffer(void){
	packet_buffer_len = 0;
	memset(packet_buffer, 0, sizeof(packet_buffer));
}

static void dump_packet(int packet_type, uint8_t * buffer, uint16_t size){
#ifdef ENABLE_PACKET_LOGGER
	static int packet_counter = 1;
	char var_name[80];
	sprintf(var_name, "test_%s_packet_%02u", packet_type == HCI_COMMAND_DATA_PACKET ? "command" : "acl", packet_counter);
	printf("uint8_t %s[] = { ", var_name);
	for (int i = 0; i < size ; i++){
		if ((i % 16) == 0) printf("\n    ");
		printf ("0x%02x, ", buffer[i]);
	}
	printf("};\n");
	packet_counter++;
#else
UNUSED(packet_type);
UNUSED(buffer);
UNUSED(size);
#endif
}

static void aes128_calc_cyphertext(uint8_t key[16], uint8_t plaintext[16], uint8_t cyphertext[16]){
	uint32_t rk[RKLENGTH(KEYBITS)];
	int nrounds = rijndaelSetupEncrypt(rk, &key[0], KEYBITS);
	rijndaelEncrypt(rk, nrounds, plaintext, cyphertext);
}

void mock_simulate_hci_event(uint8_t * packet, uint16_t size){
	dump_packet(HCI_EVENT_PACKET, packet, size);
	hci_dump_packet(HCI_EVENT_PACKET, 1, packet, size);
    // dispatch to all event handlers
    btstack_linked_list_iterator_t it;
    btstack_linked_list_iterator_init(&it, &event_packet_handlers);
    while (btstack_linked_list_iterator_has_next(&it)){
        btstack_packet_callback_registration_t * entry = (btstack_packet_callback_registration_t*) btstack_linked_list_iterator_next(&it);
        entry->callback(HCI_EVENT_PACKET, 0, packet, size);
    }
}

static void aes128_report_result(void){
	uint8_t le_enc_result[22];
	uint8_t enc1_data[] = { 0x0e, 0x14, 0x01, 0x17, 0x20, 0x00 };
	memcpy (le_enc_result, enc1_data, 6);
	reverse_128(aes128_cyphertext, &le_enc_result[6]);
	mock_simulate_hci_event(le_enc_result, sizeof(le_enc_result));
}

static void random_report_result(void){
	uint8_t le_rand_result[14];
	uint8_t rand_complete[] = { 0x0e, 0x0c, 0x01, 0x18, 0x20, 0x00 };
	memcpy (le_rand_result, rand_complete, 6);
	int i;
	for (i=0;i<8;i++){
        lfsr_random = LFSR(lfsr_random);
		le_rand_result[6+i]= lfsr_random;
	}
	mock_simulate_hci_event(le_rand_result, sizeof(le_rand_result));
}

int mock_process_hci_cmd(void){
	if (report_aes128){
		report_aes128 = 0;
		aes128_report_result();
		return 1;
	}
	if (report_random){
		report_random = 0;
		random_report_result();
		return 1;
	}
	return 0;
}

void mock_simulate_hci_state_working(void){
	uint8_t packet[] = {BTSTACK_EVENT_STATE, 0, HCI_STATE_WORKING};
	mock_simulate_hci_event((uint8_t *)&packet, sizeof(packet));
}

bool hci_can_send_command_packet_now(void){
	return true;
}

uint8_t hci_send_cmd(const hci_cmd_t *cmd, ...){
    va_list argptr;
    va_start(argptr, cmd);
    uint16_t len = hci_cmd_create_from_template(packet_buffer, cmd, argptr);
    va_end(argptr);
	hci_dump_packet(HCI_COMMAND_DATA_PACKET, 0, packet_buffer, len);
	dump_packet(HCI_COMMAND_DATA_PACKET, packet_buffer, len);
	packet_buffer_len = len;

	// track le encrypt and le rand
	if (cmd->opcode ==  hci_le_encrypt.opcode){
	    uint8_t * key_flipped = &packet_buffer[3];
	    uint8_t key[16];
		reverse_128(key_flipped, key);
	    uint8_t * plaintext_flipped = &packet_buffer[19];
	    uint8_t plaintext[16];
 		reverse_128(plaintext_flipped, plaintext);
	    aes128_calc_cyphertext(key, plaintext, aes128_cyphertext);
	    report_aes128 = 1;
#ifdef ENABLE_AES128_LOGGER
	    printf("AES128 Operation\n");
	    printf("Key:    "); printf_hexdump(key, 16);
	    printf("Plain:  "); printf_hexdump(plaintext, 16);
	    printf("Cipher: "); printf_hexdump(aes128_cyphertext, 16);
#endif
	}
	if (cmd->opcode == hci_le_rand.opcode){
		report_random = 1;
	}
	return 0;
}

void hci_add_event_handler(btstack_packet_callback_registration_t * callback_handler){
    btstack_linked_list_add_tail(&event_packet_handlers, (btstack_linked_item_t*) callback_handler);
}

HCI_STATE hci_get_state(void){
	return HCI_STATE_WORKING;
}

void btstack_run_loop_add_timer(btstack_timer_source_t * ts){
    UNUSED(ts);
}
int btstack_run_loop_remove_timer(btstack_timer_source_t * ts){
    UNUSED(ts);
	return 0;
}
void btstack_run_loop_set_timer(btstack_timer_source_t * ts, uint32_t timeout){
    UNUSED(ts);
    UNUSED(timeout);
}
void btstack_run_loop_set_timer_handler(btstack_timer_source_t * ts, void (*fn)(btstack_timer_source_t * ts)){
    UNUSED(ts);
    UNUSED(fn);
}
static void * timer_context;
void btstack_run_loop_set_timer_context(btstack_timer_source_t * ts, void * context){
    UNUSED(ts);
	timer_context = context;
}
void * btstack_run_loop_get_timer_context(btstack_timer_source_t * ts){
    UNUSED(ts);
	return timer_context;
}
void hci_halting_defer(void){
}