/**
 ******************************************************************************
 * @addtogroup OpenPilotModules OpenPilot Modules
 * @{
 * @addtogroup FlightPlan Flight Plan Module
 * @brief Executes flight plan scripts in Python
 * @{
 *
 * @file       flightplan.c
 * @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
 * @brief      Executes flight plan scripts in Python
 *
 * @see        The GNU Public License (GPL) Version 3
 *
 *****************************************************************************/
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "openpilot.h"
#include "pm.h"
#include "flightplanstatus.h"
#include "flightplancontrol.h"
#include "flightplansettings.h"

// Private constants
#define STACK_SIZE_BYTES 1500
#define TASK_PRIORITY (tskIDLE_PRIORITY+1)
#define MAX_QUEUE_SIZE 2

// Private types

// Private variables
static xTaskHandle taskHandle;
static xQueueHandle queue;

// Private functions
static void flightPlanTask(void *parameters);
static void objectUpdatedCb(UAVObjEvent * ev);

// External variables (temporary, TODO: this will be loaded from the SD card)
extern unsigned char usrlib_img[];

/**
 * Module initialization
 */
int32_t FlightPlanInitialize()
{
	taskHandle = NULL;

	// Listen for object updates
	FlightPlanControlConnectCallback(&objectUpdatedCb);

	// Create object queue
	queue = xQueueCreate(MAX_QUEUE_SIZE, sizeof(UAVObjEvent));

	// Listen for FlightPlanControl updates
	FlightPlanControlConnectQueue(queue);

	// Start VM thread
	xTaskCreate(flightPlanTask, (signed char *)"FlightPlan", STACK_SIZE_BYTES/4, NULL, TASK_PRIORITY, &taskHandle);
	TaskMonitorAdd(TASKINFO_RUNNING_FLIGHTPLAN, taskHandle);

	return 0;
}

/**
 * Module task
 */
static void flightPlanTask(void *parameters)
{
	UAVObjEvent ev;
	PmReturn_t retval;
	FlightPlanStatusData status;
	FlightPlanControlData control;

	// Setup status object
	status.Status = FLIGHTPLANSTATUS_STATUS_STOPPED;
	status.ErrorFileID = 0;
	status.ErrorLineNum = 0;
	status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_NONE;
	status.Debug[0] = 0.0;
	status.Debug[1] = 0.0;
	FlightPlanStatusSet(&status);

	// Main thread loop
	while (1)
	{
		// Wait for FlightPlanControl updates
		while (xQueueReceive(queue, &ev, portMAX_DELAY) != pdTRUE) ;

		// Get object and check if a start command was sent
		FlightPlanControlGet(&control);
		if ( control.Command == FLIGHTPLANCONTROL_COMMAND_START )
		{
			// Init PyMite
			retval = pm_init(MEMSPACE_PROG, usrlib_img);
			if (retval == PM_RET_OK)
			{
				// Update status
				FlightPlanStatusGet(&status);
				status.Status = FLIGHTPLANSTATUS_STATUS_RUNNING;
				FlightPlanStatusSet(&status);
				// Run the test script (TODO: load from SD card)
				retval = pm_run((uint8_t *)"test");
				// Check if an error or exception was thrown
				if (retval == PM_RET_OK || retval == PM_RET_EX_EXIT)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_STOPPED;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_NONE;
				}
				else if (retval == PM_RET_EX)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_EXCEPTION;
				}
				else if (retval == PM_RET_EX_IO)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_IOERROR;
				}
				else if (retval == PM_RET_EX_ZDIV)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_DIVBYZERO;
				}
				else if (retval == PM_RET_EX_ASSRT)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_ASSERTERROR;
				}
				else if (retval == PM_RET_EX_ATTR)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_ATTRIBUTEERROR;
				}
				else if (retval == PM_RET_EX_IMPRT)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_IMPORTERROR;
				}
				else if (retval == PM_RET_EX_INDX)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_INDEXERROR;
				}
				else if (retval == PM_RET_EX_KEY)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_KEYERROR;
				}
				else if (retval == PM_RET_EX_MEM)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_MEMORYERROR;
				}
				else if (retval == PM_RET_EX_NAME)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_NAMEERROR;
				}
				else if (retval == PM_RET_EX_SYNTAX)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_SYNTAXERROR;
				}
				else if (retval == PM_RET_EX_SYS)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_SYSTEMERROR;
				}
				else if (retval == PM_RET_EX_TYPE)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_TYPEERROR;
				}
				else if (retval == PM_RET_EX_VAL)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_VALUEERROR;
				}
				else if (retval == PM_RET_EX_STOP)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_STOPITERATION;
				}
				else if (retval == PM_RET_EX_WARN)
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_WARNING;
				}
				else
				{
					status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
					status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_UNKNOWNERROR;
				}
				// Get file ID and line number of error (if one)
				status.ErrorFileID = gVmGlobal.errFileId;
				status.ErrorLineNum = gVmGlobal.errLineNum;
			}
			else
			{
				status.Status = FLIGHTPLANSTATUS_STATUS_ERROR;
				status.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_VMINITERROR;
			}

			// Update status object
			FlightPlanStatusSet(&status);
		}
	}
}

/**
 * Function called in response to object updates.
 * Used to force kill the VM thread.
 */
static void objectUpdatedCb(UAVObjEvent * ev)
{
	FlightPlanControlData controlData;
	FlightPlanStatusData statusData;

	// If the object updated was the FlightPlanControl execute requested action
	if ( ev->obj == FlightPlanControlHandle() )
	{
		// Get data
		FlightPlanControlGet(&controlData);
		// Execute command
		if ( controlData.Command == FLIGHTPLANCONTROL_COMMAND_START )
		{
			// Start VM task if not running already
			if ( taskHandle == NULL )
			{
				xTaskCreate(flightPlanTask, (signed char *)"FlightPlan", STACK_SIZE_BYTES/4, NULL, TASK_PRIORITY, &taskHandle);
				TaskMonitorAdd(TASKINFO_RUNNING_FLIGHTPLAN, taskHandle);
			}
		}
		else if ( controlData.Command == FLIGHTPLANCONTROL_COMMAND_KILL )
		{
			// Force kill VM task if it is already running
			// (NOTE: the STOP command is preferred as it allows the script to terminate without killing the VM)
			if ( taskHandle != NULL )
			{
				// Kill VM
				TaskMonitorRemove(TASKINFO_RUNNING_FLIGHTPLAN);
				vTaskDelete(taskHandle);
				taskHandle = NULL;
				// Update status object
				statusData.Status = FLIGHTPLANSTATUS_STATUS_STOPPED;
				statusData.ErrorFileID = 0;
				statusData.ErrorLineNum = 0;
				statusData.ErrorType = FLIGHTPLANSTATUS_ERRORTYPE_NONE;
				statusData.Debug[0] = 0.0;
				statusData.Debug[1] = 0.0;
				FlightPlanStatusSet(&statusData);
			}
		}
	}
}

/**
  * @}
  * @}
  */
