/*	
 *	cliptask.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001 Naoyuki Sawa
 *
 *	* Fri Nov 30 14:58:56 JST 2001 Naoyuki Sawa
 *	- 作成開始。
 *	* Fri Nov 30 16:32:28 JST 2001 Naoyuki Sawa
 *	- 1stリリース。
 *	* Wed Jul 17 15:28:00 JST 2002 Naoyuki Sawa
 *	- TASK.suspendに自動レジューム機能を追加しました。
 */
#include "clip.h"

/*****************************************************************************
 *	TASK
 *****************************************************************************/

TASK*
task_new(TASK* parent, int data_size)
{
	TASK *task, *task_pos;

	/* メモリを確保します。 */
	task = (TASK*)calloc(1, sizeof(TASK) + data_size);
	if(task == NULL) return NULL; /* 失敗 */

	/* トップレベルタスクでなければ、
	 * 親タスクへのポインタを格納し、
	 * 親タスクのタスクリストに追加します。
	 */
	if(parent != NULL) {
		task->parent = parent;
		if((task_pos = parent->children) == NULL) {
			parent->children = task;
		} else {
			while(task_pos->siblings != NULL) {
				task_pos = task_pos->siblings;
			}
			task_pos->siblings = task;
		}
	}

	return task;
}

void
task_free(TASK* task)
{
	TASK *parent, *task_pos;

	/* Freeハンドラがあれば、呼び出します。 */
	if(task->free != NULL) task->free(task);

	/* 全ての子タスクを開放します。 */
	task_clear(task);

	/* トップレベルタスクでなければ、
	 * 親タスクのタスクリストから削除します。
	 */
	if((parent = task->parent) != NULL) {
		if((task_pos = parent->children) == task) {
			parent->children = task->siblings;
		} else {
			while(task_pos->siblings != task) {
				task_pos = task_pos->siblings;
			}
			task_pos->siblings = task->siblings;
		}
	}

	/* メモリを開放します。 */
	free(task);
}

void
task_clear(TASK* task)
{
	TASK* task_pos;

	/* タスクリストが空になるまで、
	 * タスクリストの先頭タスクを開放します。
	 */
	while((task_pos = task->children) != NULL) {
		task_free(task_pos);
	}
}

int
task_work(TASK* task)
{
	TASK *task_pos, *task_next;

	/* サスペンド状態なら、何もせずに帰ります。 */
	if(task->suspend != 0) {
		if(task->suspend > 0) task->suspend--; /* 自動レジューム */
		return 1;
	}

	/* Workハンドラがあれば、呼び出します。
	 * Workハンドラが0を返したら、タスクを開放します。
	 */
	if(task->work != NULL) {
		if(!task->work(task)) {
			task_free(task);
			return 0;
		}
	}

	/* タスクリストを再起的に呼び出します。 */
	task_pos = task->children;
	while(task_pos != NULL) {
		/* task_work(task_pos)呼び出しにより、
		 * task_posは開放される可能性があるので、
		 * あらかじめ次のタスクを取得しておきます。
		 */
		task_next = task_pos->siblings;
		task_work(task_pos);
		task_pos = task_next;
	}

	return 1;
}

void
task_draw(TASK* task)
{
	TASK* task_pos;

	/* Drawハンドラがあれば、呼び出します。 */
	if(task->draw != NULL) task->draw(task);

	/* タスクリストを再起的に呼び出します。 */
	task_pos = task->children;
	while(task_pos != NULL) {
		task_draw(task_pos);
		task_pos = task_pos->siblings;
	}
}

int
task_count(TASK* task)
{
	if(task == NULL) return 0;
	return 1 +				/* 自分 */
	       task_count(task->siblings) +	/* 兄弟 */
	       task_count(task->children);	/* 子孫 */
}
