/*
 * Kangaroo Punch MultiPlayer Game Server Mark II
 * Copyright (C) 2020-2021  Scott Duensing
 *
 * 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, see <https://www.gnu.org/licenses/>.
 *
 */


#define MINICORO_IMPL
#define MCO_USE_ASM
//#define MCO_DEFAULT_STACK_SIZE 114688 // Default is 57344
#include "thirdparty/minicoro/minicoro.h"

#include "task.h"
#include "array.h"


typedef struct TaskS {
	mco_coro  *coroutine;
	void     (*function)(void *);
	void      *data;
} TaskT;


static TaskT **_taskList = NULL;


static void taskHandle(mco_coro* coroutine);


uint8_t taskCreate(void (*function)(void *), void *data) {
	mco_desc    desc = mco_desc_init(taskHandle, 0);
	mco_result  res  = 0;
	TaskT      *task = (TaskT *)malloc(sizeof(TaskT));

	if (task) {
		task->function = function;
		task->data     = data;
		desc.user_data = task;
		res = mco_create(&task->coroutine, &desc);
		if (res != MCO_SUCCESS) {
			mco_destroy(task->coroutine);
			free(task);
			return 1;  // Failed
		}
		arrput(_taskList, task);
	}

	return 1;  // Failed
}


static void taskHandle(mco_coro* coroutine) {
	TaskT *task = (TaskT *)mco_get_user_data(coroutine);
	task->function(task->data);
}


void taskProxy(WidgetT *widget) {
	taskFunction task = (taskFunction)guiUserDataGet(widget);
	taskCreate(task, NULL);
}


void taskRun(void) {
	uint16_t   taskIndex = 0;

	// Run until there are no more tasks.
	while (arrlen(_taskList) > 0) {

		// Run each task in order.
		taskIndex = 0;
		while (taskIndex < arrlen(_taskList)) {

			// Run task.
			mco_resume(_taskList[taskIndex]->coroutine);
			// Did it finish?
			if (mco_status(_taskList[taskIndex]->coroutine) == MCO_DEAD) {
				// Task ended.  Remove it.
				mco_destroy(_taskList[taskIndex]->coroutine);
				free(_taskList[taskIndex]);
				arrdel(_taskList, taskIndex);
			} else {
				// Next task.
				taskIndex++;
			}

		} // while each task

	} // while tasks exist

	arrfree(_taskList);
	_taskList = NULL;
}


void taskShutdown(void) {
	// Nada
}


void taskStartup(void) {
	// Nada
}


void __attribute__ ((noinline)) taskYield(void) {
	mco_yield(mco_running());
}
