/*
 * HydraBus/HydraNFC
 *
 * Copyright (C) 2017 Benjamin VERNOUX
 *
 * 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.
 */

#include <string.h>
#include <stdio.h> /* sprintf */
#include "ch.h"
#include "hal.h"

#include "chprintf.h"
#include "common.h"

#include "hydrabus.h"
#include "bsp_gpio.h"
#include "bsp.h"

#include "bsp_uart.h"

/* Convert MCU cycle counter to microsecond (see get_cyclecounter64()/get_cyclecounter()) (with MCU@168MHz) */
#define CYCLES2US(cycles) ((uint64_t)cycles / (uint64_t)168ULL)

/* USB1: Virtual serial port over USB. */
extern SerialUSBDriver SDU1;
/* USB2: Virtual serial port over USB. */
extern SerialUSBDriver SDU2;

#define UART_NANO (BSP_DEV_UART1)
#define BLINK_ULED_MILLISEC	30

/* TODO find NB_CHAR_PASSWD */
#define NB_CHAR_PASSWD (1) /* Number of character in password including \r */

static mode_config_proto_t uart1_mode_conf;

void usb_printf(const char *fmt, ...)
{
	va_list va_args;
	int real_size;
#define TPRINTF_BUFF_SIZE (255)
	char tprintf_buff[TPRINTF_BUFF_SIZE+1];

	va_start(va_args, fmt);
	real_size = vsnprintf(tprintf_buff, TPRINTF_BUFF_SIZE, fmt, va_args);
	va_end(va_args);

	if (SDU1.config->usbp->state == USB_ACTIVE)
		chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)tprintf_buff, real_size);

	if (SDU2.config->usbp->state == USB_ACTIVE)
		chnWrite((BaseSequentialStream*)&SDU2, (uint8_t *)tprintf_buff, real_size);

	/* Wait a bit in order to display all */
	chThdSleepMilliseconds(2);
}

void uart_write(int dev_num, uint8_t* tx_data, uint8_t nb_data)
{
	bsp_uart_write_u8(dev_num, tx_data, nb_data);
}

void uart_rx_flush(int dev_num)
{
	uint8_t rx_data[2] = { 0 }; 
	int timeout_max_ms = 10;
	int nb_ms = 0;

	while(bsp_uart_rxne(dev_num) == 1)
	{
		rx_data[0] = 0;
		bsp_uart_read_u8_timeout(dev_num, rx_data, 1, MS2ST(1));
		nb_ms++;
		if(nb_ms > timeout_max_ms)
			return;
	}
}

bsp_status_t uart_read(int dev_num, uint8_t* rx_data, uint8_t nb_data, uint32_t timeout_ms)
{
	if( bsp_uart_read_u8_timeout(dev_num, rx_data, nb_data, timeout_ms) == BSP_OK )
	{
		return BSP_OK;
	}else
	{
		return BSP_TIMEOUT;
	}
}

void main_init(void)
{
	int i;
	/* Configure PC0 & PC1 as Output and state 0 (used for debug) */
	for(i = 0; i < 2; i++){
		bsp_gpio_clr(BSP_GPIO_PORTC, i);
		bsp_gpio_init(BSP_GPIO_PORTC, i, MODE_CONFIG_DEV_GPIO_OUT_PUSHPULL, MODE_CONFIG_DEV_GPIO_NOPULL);
	}

	/* TODO find speed, parity & nb stop bit */
	/* Configure USART1 (PA9=TX / PA10=RX)
		 Connected to Arduino Nano ATmega328p UART
	*/
	uart1_mode_conf.dev_num = UART_NANO;
	uart1_mode_conf.dev_speed = 0; /* TODO speed */
	uart1_mode_conf.dev_parity = 0; /* TODO parity */
	uart1_mode_conf.dev_stop_bit = 0; /* TODO nb stop bit */
	bsp_uart_init(UART_NANO, &uart1_mode_conf);
}

void main_cleanup(void)
{
	int i;
	for(i = 0; i < 2; i++){
		bsp_gpio_init(BSP_GPIO_PORTC, i, MODE_CONFIG_DEV_GPIO_IN, MODE_CONFIG_DEV_GPIO_NOPULL);
	}
	bsp_uart_deinit(UART_NANO);
}

/*
 Solve/bruteforce each char of the fixed len password
 Can be stopped if USER_BUTTON is pressed return -1
return NB step solved
*/
int main_solve(void)
{
	int nb_solved;
	uint64_t tick_start;
	uint64_t tick_end;
	uint64_t tick_delta;
	uint32_t total_time_ms;

	usb_printf("main_solve start\r\n");

	tick_start = get_cyclecounter64();

	nb_solved = 0;
	while(nb_solved < (NB_CHAR_PASSWD-1))
	{
		/* TODO Code to solve the challenge */

		if(USER_BUTTON)
			break;
	}

	tick_end = get_cyclecounter64();

	tick_delta = tick_end - tick_start;
	total_time_ms = (uint32_t)(CYCLES2US(tick_delta) / 1000ULL);
	usb_printf("Total Time %ds (%dms)\r\n", (total_time_ms / 1000), total_time_ms);
	usb_printf("main_solve end\r\n");

	if(USER_BUTTON)
		return -1;

	return nb_solved;
}

void main_run(void)
{
	int i;
	
	/* Blink ULED 5 times to indicate we start */
	for(i = 0; i < 5; i++)
	{
		ULED_ON;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
		ULED_OFF;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
	}

	chThdSleepMilliseconds(1);
	/* Wait until USER_BUTTON is released */
	while (USER_BUTTON)
	{
		ULED_ON;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
		ULED_OFF;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
	}

	usb_printf("main_run start\r\n");

	main_init();

	main_solve();

	main_cleanup();

	/* Blink ULED 10 times to indicate we exit */
	for(i = 0; i < 10; i++)
	{
		ULED_ON;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
		ULED_OFF;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
	}

	/* Wait until USER_BUTTON is released */
	while (USER_BUTTON)
	{
		ULED_ON;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
		ULED_OFF;
		chThdSleepMilliseconds(BLINK_ULED_MILLISEC);
	}

	usb_printf("main_run end\r\n");
}
