//////////////////////////////////////////////////////////////////
//
// App:			Controller
// Author:		Milan Korinek
// Version:		1.0
// Notice:		Main control of Digital I/O USB Card
//////////////////////////////////////////////////////////////////

#include "controller.h"
#include "support/DIO_Ports.h"

typedef struct {
	xQueueHandle control_pipe;	
	run_led_handle_t  * run_led_handle;
	ports_controller_handle_t  * ports_controller_handle;
} controller_task_param_t;

bool get_basic_configuration(controller_task_param_t * task_param);

const char *const controller_msg_type_str[] = {
	FOREACH_controller_msg_type(GENERATE_STRING)
};

status_code_t controller_init(_In_ run_led_handle_t  * run_led_handle, 
							  _In_ ports_controller_handle_t  * ports_controller_handle, 
							  _Out_ controller_handle_t * task_handle)
{
	portBASE_TYPE retTask;
	
	static controller_task_param_t task_param;
  
	task_param.control_pipe = 0;
	task_param.run_led_handle = run_led_handle;
	task_param.ports_controller_handle = ports_controller_handle;
	
	task_handle->task_handle = NULL;
	task_handle->control_pipe = 0;
	
	/* Create FreeRTOS queue for task control pipe */
	task_param.control_pipe = xQueueCreate(TASK_CONTROLLER_CONTROL_PIPE_LEN, sizeof(controller_task_param_t));
	if (task_param.control_pipe == 0) {
		return ERR_NO_MEMORY;
	}
	task_handle->control_pipe = task_param.control_pipe;

	/* Create the task. */
	retTask = xTaskCreate(controller_task, (const signed char *const) TASK_CONTROLLER_NAME,
		TASK_CONTROLLER_STACK_DEPTH_WORDS, (void *)(& task_param), TASK_CONTROLLER_PRIORITY, &(task_handle->task_handle));
		
	if ((retTask != pdPASS) || (task_handle->task_handle == NULL))	{
		return ERR_NO_MEMORY;
	}
	
	return STATUS_OK;
}

void controller_task(_In_ void *pvParameters)
{
	controller_task_param_t * task_param = (controller_task_param_t *)pvParameters;
	controller_msg_t control_msg;
	run_led_msg_t msgLED;
	bool retValue;
	
	debug_event(DIO_SEVERITY_IMPORTANT, "Task 'Controller' running...");
	for(;;)	{
		if (xQueueReceive(task_param->control_pipe, (void *)(&control_msg) , portMAX_DELAY) == pdPASS) { /* Nothing to do if queue is empty */
			switch (control_msg.message) {
				case CONTROLLER_BASIC_CONFIG:
					debug_warning(DIO_SEVERITY_IMPORTANT, "New control message arrive: %s.", controller_msg_type_str[control_msg.message]);
					retValue = get_basic_configuration(task_param);
					if (retValue) 
						DeviceStateInt.DeviceState = E_DEVICE_STATE_CONFIGURED;
					else
						DeviceStateInt.DeviceState = E_DEVICE_STATE_CLOSE;
					break;
				default:
					debug_warning(DIO_SEVERITY_IMPORTANT, "Unknown type of control message %d.", control_msg.message);
					retValue = false;
					break;
					
			}
			
			// UNLOCK buffer
			portENTER_CRITICAL();
			buffer_control_lock = false;
			portEXIT_CRITICAL();
			
			if (!retValue) {
				msgLED.message = RUN_LED_MSG_ERROR;
				xQueueSend(task_param->run_led_handle->control_pipe, (void *)(& msgLED), MAX_WAIT_TO_SET_PORTS);
			}
		}
	}

	/* If all is well, the following line will never be reached. */
	vTaskDelete(NULL);
}



bool get_basic_configuration(controller_task_param_t * task_param)
{
	uint32_t packet_length =  buffer_control[0];
	uint32_t i, port_pull_res_enable = 0, port_pull_res_set = 0, port_dir = 0 ;
	ports_controller_msg_t msgPC;
	run_led_msg_t msgLED;
	portBASE_TYPE retValue;
	
	
	// Parse receive packet
	retValue = ParseDeviceConfigurationPacket(& buffer_control[1], packet_length, &DeviceStateInt.DeviceState, 
		&DeviceStateInt.DeviceBehavior, DeviceStateInt.PortsConfiguration);	
	if (!retValue) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not parse packet.");
		PrintRawPacket(& buffer_control[1], packet_length);
		return false;
	}
	
	// Create settings of ports
	debug_info(DIO_SEVERITY_IMPORTANT, "device state = %d, behavior mode = %d, connection state = %d ", 
		DeviceStateInt.DeviceState, DeviceStateInt.DeviceBehavior.Mode, DeviceStateInt.DeviceConnectionState);
	for (i = 0; i < DIO_MAX_PORTS; i++){
		debug_info(DIO_SEVERITY_IMPORTANT, "default output = 0x%X, port dir = %s, resistors = %s",
			DeviceStateInt.PortsConfiguration[i].OutputDefault, 
			(DeviceStateInt.PortsConfiguration[i].PortDirection == DIO_E_PORTDIR_INPUT) ? "Inputs" : "Outputs",
			(DeviceStateInt.PortsConfiguration[i].PullResistors == DIO_E_PULLRES_PULLUP) ? "PullUp" : "PullDown");
		
		port_pull_res_enable |= get_ports_pull_res_enable(DeviceStateInt.PortsConfiguration[i].PullResistors != DIO_E_PULLRES_HIGH_Z, i);
		port_pull_res_set |= get_ports_pull_res(DeviceStateInt.PortsConfiguration[i].PullResistors == DIO_E_PULLRES_PULLUP, i);
		port_dir |= get_ports_dir(DeviceStateInt.PortsConfiguration[i].PortDirection == DIO_E_PORTDIR_OUTPUT, i);
		
		// Configure mcu I/O
		DIO_configure_port(i, DeviceStateInt.PortsConfiguration[i]);
	}
	
	// Create message, Power Off - (mcu outputs and transceivers outputs together are bad)
	msgPC.message = PORTS_CNTR_MSG_PWR_SET;
	msgPC.enable = false;
	if (xQueueSend(task_param->ports_controller_handle->control_pipe, (void *)(& msgPC), MAX_WAIT_TO_SET_PORTS) != pdTRUE) return false;
	
	// Create message, Set Ports Pull-Up resistors
	msgPC.message = PORTS_CNTR_MSG_PULL_RES_SET;
	msgPC.enable = port_pull_res_enable;
	msgPC.settings = port_pull_res_set;
	msgPC.devices = IOEXP_PX_PULL_RES_MASK;
	if (xQueueSend(task_param->ports_controller_handle->control_pipe, (void *)(& msgPC), MAX_WAIT_TO_SET_PORTS) != pdTRUE) return false;
	
	// Create message, Set Ports Direction
	msgPC.message = PORTS_CNTR_MSG_PULL_RES_SET;
	msgPC.settings = port_dir;
	msgPC.devices = IOEXP_PX_DIR_MASK;
	if (xQueueSend(task_param->ports_controller_handle->control_pipe, (void *)(& msgPC), MAX_WAIT_TO_SET_PORTS) != pdTRUE) return false;
	
	// Create message, Power On
	msgPC.message = PORTS_CNTR_MSG_PWR_SET;
	msgPC.enable = true;
	if (xQueueSend(task_param->ports_controller_handle->control_pipe, (void *)(& msgPC), MAX_WAIT_TO_SET_PORTS) != pdTRUE) return false;
	
	// Set LED
	if (DeviceStateInt.DeviceBehavior.Mode == DIO_E_MODE_ACCES_DIO){
		msgLED.message = RUN_LED_MSG_RUN;
	} else {
		msgLED.message = RUN_LED_MSG_ON;
	}
	
	if (xQueueSend(task_param->run_led_handle->control_pipe, (void *)(& msgLED), MAX_WAIT_TO_SET_PORTS) != pdTRUE) return false;
	
	
	return true;
}