/*
 * OpenMRCP - Open Source Media Resource Control Protocol Stack
 * Copyright (C) 2007, Cepstral LLC
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Author(s):
 * Arsen Chaloyan <achaloyan@yahoo.com>
 *
 * Contributor(s):
 *
 */

#include <assert.h>
#include <apr_thread_proc.h>
#include <apr_thread_cond.h>
#include "apt_task.h"

typedef struct apt_task_event_slot_t apt_task_event_slot_t;

struct apt_task_event_slot_t {
	void                   *data;
	apt_task_event_handler handler;
};

struct apt_task_t {
	apr_pool_t           *pool;
	apr_thread_mutex_t   *data_guard;
	apr_thread_t         *thread_handle;
	apt_task_state_t      state;

	apt_task_event_slot_t main;
	apt_task_event_slot_t event_handlers[TASK_STATE_COUNT];
};


static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *data);

static APR_INLINE void apt_task_event_raise(apt_task_t *task, apt_task_state_t task_event)
{
	if(task->event_handlers[task_event].handler) {
		task->event_handlers[task_event].handler(task->event_handlers[task_event].data);
	}
}

APT_DECLARE(apt_task_t*) apt_task_create(void *data, apt_task_event_handler main, apr_pool_t *pool)
{
	apr_size_t i;
	apt_task_t *task = apr_palloc(pool,sizeof(apt_task_t));
	task->pool = pool;

	task->state = TASK_STATE_NONE;
	task->thread_handle = NULL;
	if(apr_thread_mutex_create(&task->data_guard, APR_THREAD_MUTEX_DEFAULT, task->pool) != APR_SUCCESS) {
		return NULL;
	}

	task->main.data = data;
	task->main.handler = main;

	for(i=0; i<TASK_STATE_COUNT; i++) {
		task->event_handlers[i].data = NULL;
		task->event_handlers[i].handler = NULL;
	}
	return task;
}

APT_DECLARE(apt_bool_t) apt_task_destroy(apt_task_t *task)
{
	apt_bool_t wait_until_terminate = FALSE;
	apr_thread_mutex_lock(task->data_guard);
	if(task->state != TASK_STATE_NONE) {
		wait_until_terminate = TRUE;
	}
	apr_thread_mutex_unlock(task->data_guard);
	if(wait_until_terminate == TRUE) {
		apt_task_wait_until_terminate(task);
	}

	apr_thread_mutex_destroy(task->data_guard);
	return TRUE;
}

APT_DECLARE(apt_bool_t) apt_task_start(apt_task_t *task)
{
	apt_bool_t status = TRUE;
	apr_thread_mutex_lock(task->data_guard);
	if(task->state == TASK_STATE_NONE || task->state == TASK_STATE_TERMINATE_COMPLETED) {
		apr_status_t rv;
		task->state = TASK_STATE_START_REQUESTED;
		apt_task_event_raise(task,task->state);
		rv = apr_thread_create(&task->thread_handle,NULL,apt_task_run,task,task->pool);
		if(rv != APR_SUCCESS) {
			task->state = TASK_STATE_NONE;
			status = FALSE;
		}
	}
	else {
		status = FALSE;
	}
	apr_thread_mutex_unlock(task->data_guard);
	return status;
}

APT_DECLARE(apt_bool_t) apt_task_terminate(apt_task_t *task, apt_bool_t wait_until_terminate)
{
	apr_thread_mutex_lock(task->data_guard);
	if(task->state == TASK_STATE_START_REQUESTED || task->state == TASK_STATE_START_IN_PROGRESS || task->state == TASK_STATE_START_COMPLETED) {
		task->state = TASK_STATE_TERMINATE_REQUESTED;
	}
	apr_thread_mutex_unlock(task->data_guard);

	if(task->state == TASK_STATE_TERMINATE_REQUESTED) {
		apt_task_event_raise(task,task->state);

		if(wait_until_terminate == TRUE) {
			apt_task_wait_until_terminate(task);
		}
	}

	return TRUE;
}

APT_DECLARE(apt_bool_t) apt_task_wait_until_terminate(apt_task_t *task)
{
	if(task->thread_handle) {
		apr_status_t s;
		apr_thread_join(&s,task->thread_handle);
	}
	return TRUE;
}

APT_DECLARE(void) apt_task_delay(apr_size_t msec)
{
	apr_sleep(1000*msec);
}

APT_DECLARE(apt_bool_t) apt_task_event_handler_set(apt_task_t *task, apt_task_state_t task_event,
                                        void *data, apt_task_event_handler handler)
{
	if(task_event >= TASK_STATE_COUNT) {
		return FALSE;
	}
	task->event_handlers[task_event].data = data;
	task->event_handlers[task_event].handler = handler;
	return TRUE;
}

APT_DECLARE(apt_task_state_t) apt_task_state_set(apt_task_t *task, apt_task_state_t state)
{
	task->state = state;
	apt_task_event_raise(task,task->state);
	return TRUE;
}

APT_DECLARE(apt_task_state_t) apt_task_state_get(apt_task_t *task)
{
	return task->state;
}

APT_DECLARE(void*) apt_task_data_get(apt_task_t *task)
{
	return task->main.data;
}

static void* APR_THREAD_FUNC apt_task_run(apr_thread_t *thread_handle, void *data)
{
	apt_task_t *task = data;
	assert(task);
	
	apr_thread_mutex_lock(task->data_guard);
	task->state = TASK_STATE_START_IN_PROGRESS;
	apr_thread_mutex_unlock(task->data_guard);

	apt_task_event_raise(task,task->state);

	if(task->main.handler) {
		task->main.handler(task->main.data);
	}
	
	apr_thread_mutex_lock(task->data_guard);
	task->state = TASK_STATE_TERMINATE_COMPLETED;
	apr_thread_mutex_unlock(task->data_guard);

	apt_task_event_raise(task,task->state);
	
	apr_thread_mutex_lock(task->data_guard);
	task->state = TASK_STATE_NONE;
	apr_thread_mutex_unlock(task->data_guard);
	return NULL;
}
