#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <iiimp.h>

#include "input-method.h"
#include "input-context.h"
#include "handler.h"


IIIMF_status
iiimf_message_handler_entry_new(
    IIIMF_message_handler_entry ***	entry_ret)
{
    IIIMF_message_handler_entry **	entry;
    size_t				size;
    int					i;

    size = ((sizeof (IIIMF_message_handler_entry *)) * 128);

    entry = (IIIMF_message_handler_entry **)malloc(size);
    if (NULL == entry) return IIIMF_STATUS_MALLOC;

    for (i = 0; i < 128; i++) {
	*(entry + i) = NULL;
    }

    *entry_ret = entry;

    return IIIMF_STATUS_SUCCESS;
}


void
iiimf_message_handler_entry_delete(
    IIIMF_message_handler_entry **	entry)
{
    IIIMF_message_handler_entry *	e;
    IIIMF_message_handler_entry *	e_next;
    int					i;

    for (i = 0; i < 128; i++) {
	for (e = *(entry + i); NULL != e; e = e_next) {
	    e_next = e->next;
	    iiimf_message_handler_entry_item_delete(e);
	}
    }

    free(entry);
}


IIIMF_message_handler_entry *
iiimf_message_handler_entry_item_new(
    const char *		name,
    IIIMF_message_handler	proc)
{
    IIIMF_message_handler_entry *	entry;

    entry = (IIIMF_message_handler_entry *)
	malloc(sizeof (IIIMF_message_handler_entry));
    if (NULL == entry) return NULL;

    entry->name = strdup(name);
    if (NULL == entry->name) {
	free(entry);
	return NULL;
    }

    entry->proc = proc;
    entry->next = NULL;

    return entry;
}


void
iiimf_message_handler_entry_item_delete(
    IIIMF_message_handler_entry *	entry)
{
    free(entry->name);
    free(entry);
}


IIIMF_status
iiimf_message_handler_register(
    IIIMF_im *					im,
    int						opcode,
    IIIMF_message_handler			handler,
    const char *				name,
    IIIMF_message_handler_register_place	place,
    const char *				anchor)
{
    IIIMF_message_handler_entry *	entry;
    IIIMF_message_handler_entry *	entry_prev;
    IIIMF_message_handler_entry *	entry_new;

    if (0x7f < opcode) return IIIMF_STATUS_OPCODE;

    entry_new = iiimf_message_handler_entry_item_new(name, handler);
    if (NULL == entry_new) return IIIMF_STATUS_MALLOC;

    entry = *(im->message_handler + opcode);

    switch (place) {
    case IIIMF_MESSAGE_HANDLER_REGISTER_PLACE_HEAD:
	entry = NULL;
	break;
    case IIIMF_MESSAGE_HANDLER_REGISTER_PLACE_TAIL:
	if (NULL != entry) {
	    for (; NULL != entry->next; entry = entry->next) {
	    }
	}
	break;
    case IIIMF_MESSAGE_HANDLER_REGISTER_PLACE_BEFORE:
	entry_prev = NULL;
	for (; NULL != entry; entry = entry->next) {
	    if (0 == strcmp(entry->name, anchor)) break;
	    entry_prev = entry;
	}
	if (NULL == entry) {
	    iiimf_message_handler_entry_item_delete(entry_new);
	    return IIIMF_STATUS_ARGUMENT;
	}
	entry = entry_prev;
	break;
    case IIIMF_MESSAGE_HANDLER_REGISTER_PLACE_AFTER:
	for (; NULL != entry; entry = entry->next) {
	    if (0 == strcmp(entry->name, anchor)) break;
	}
	if (NULL == entry) {
	    iiimf_message_handler_entry_item_delete(entry_new);
	    return IIIMF_STATUS_ARGUMENT;
	}
	break;
    default:
	return IIIMF_STATUS_ARGUMENT;
    }

    if (NULL == entry) {
	entry_new->next = *(im->message_handler + opcode);
	*(im->message_handler + opcode) = entry_new;
    } else {
	entry_new->next = entry->next;
	entry->next = entry_new;
    }

    return IIIMF_STATUS_SUCCESS;
}


IIIMF_status
iiimf_message_handler_unregister(
    IIIMF_im *			im,
    int				opcode,
    IIIMF_message_handler	handler)
{
    IIIMF_message_handler_entry *	entry;
    IIIMF_message_handler_entry *	entry_next;
    IIIMF_message_handler_entry *	entry_prev;

    if (0x7f < opcode) return IIIMF_STATUS_OPCODE;

    entry = *(im->message_handler + opcode);

    if (NULL == entry->proc) return IIIMF_STATUS_ARGUMENT;

    if (handler == entry->proc) {
	entry_next = entry->next;
	if (NULL != entry_next) {
	    entry->proc = entry_next->proc;
	    entry->next = entry_next->next;
	    free(entry_next);
	}
	return IIIMF_STATUS_SUCCESS;
    }

    for (entry_prev = entry, entry = entry->next;
	 NULL != entry;
	 entry_prev = entry, entry = entry->next) {
	if (handler == entry->proc) {
	    entry_prev->proc = entry->next->proc;
	    entry_prev->next = entry->next->next;
	    free(entry);
	    return IIIMF_STATUS_SUCCESS;
	}
    }

    return IIIMF_STATUS_ARGUMENT;
}


IIIMF_status
iiimf_message_handler_unregister_name(
    IIIMF_im *		im,
    const char *	name)
{
    IIIMF_status			status;
    int					opcode;
    IIIMF_message_handler_entry *	entry;
    IIIMF_message_handler_entry *	entry_prev;

    status = IIIMF_STATUS_FAIL;

    for (opcode = 0; opcode < 128; opcode++) {
	entry = *(im->message_handler + opcode);
	if (NULL == entry) continue;

	entry_prev = NULL;
	for (; NULL != entry; entry = entry->next) {
	    if (0 == strcmp(entry->name, name)) break;
	    entry_prev = entry;
	}
	if (NULL != entry) {
	    if (NULL == entry_prev) {
		*(im->message_handler + opcode) = entry->next;
	    } else {
		entry_prev->next = entry->next;
	    }
	    iiimf_message_handler_entry_item_delete(entry);
	    status = IIIMF_STATUS_SUCCESS;
	}
    }

    return status;
}


IIIMF_status
iiimf_message_handler_call(
    IIIMF_im *		im,
    IIIMP_message *	message)
{
    IIIMF_ic *				ic;
    IIIMF_message_handler_status	status;
    IIIMF_message_handler_entry *	entry;

    entry = *(im->message_handler + message->opcode);
    if ((NULL == entry) || (NULL == entry->proc)) {
	return IIIMF_STATUS_SUCCESS;
    }

    if (0 <= message->ic_id) {
	for (ic = im->ic_list; NULL != ic; ic = ic->next) {
	    if ( message->ic_id == ic->ic_id) break;
	}
	if (NULL == ic) return IIIMF_STATUS_INVALID_ID;
    } else {
	ic = NULL;
    }

    for (; NULL != entry; entry = entry->next) {
	status = (*(entry->proc))(im, ic, message);
	if (IIIMF_MESSAGE_HANDLER_STATUS_NORMAL == status) {
	    continue;
	} else if (IIIMF_MESSAGE_HANDLER_STATUS_STOP == status) {
	    return IIIMF_STATUS_SUCCESS;
	} else {
	    return IIIMF_STATUS_FAIL;
	}
    }

    return IIIMF_STATUS_SUCCESS;
}


/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
