#include "lib_acl.h"
#include "service.h"
#include "service_conf.h"
#include "service_ipc.h"
#include "service_main.h"

/*----------------------------------------------------------------------------*/

static ACL_FIFO *__service_modules = NULL;
static SERVICE *__service = NULL;
static module_service_main_fn __service_callback = NULL;
static ACL_DLL_ENV  __dll_env;
ACL_MEM_SLICE *var_mem_slice = NULL;

static SERVICE *service_create(ACL_AIO *aio,
	ACL_FIFO *service_modules, ACL_ARGV *ip_argv)
{
	const char *myname = "service_create";
	ACL_FIFO *services;
	SERVICE *service, *curr_service = NULL;
	ACL_ITER iter;

	/* رճ */
	acl_aio_set_keep_read(aio, 0);

	services = acl_fifo_new();

	/* еķģ */
	acl_foreach(iter, service_modules) {
		MODULE_SERVICE *module = (MODULE_SERVICE*) iter.data;

		service = module->mod_create();
		if (service == NULL)
			continue;
		service->aio = aio;
		service->module = module;
		service->rw_timeout = 120;
		service->conn_timeout = 10;
		service_set_dns(service, aio,
				var_cfg_dns_list,
				var_cfg_dns_lookup_timeout,
				var_cfg_dns_cache_limit,
				var_cfg_hosts_list);
		/* ӳػ */
		service->conn_cache = conn_cache_create(aio, var_cfg_server_conn_limit);

		if (ip_argv) {
			ACL_ITER iter2;
			int   i = 0;

			service->bind_ip_list = (char**)
				acl_mycalloc(ip_argv->argc + 1, sizeof(char*));
			service->bind_ip_index = 0;

			acl_foreach(iter2, ip_argv) {
				char *ip = (char*) iter2.data;
				service->bind_ip_list[i++] = acl_mystrdup(ip);
			}
			service->bind_ip_list[i] = NULL;
		} else {
			service->bind_ip_list = NULL;
			service->bind_ip_index = -1;
		}
		acl_fifo_push(services, service);
	}

	if (acl_fifo_size(service_modules) == 0)
		acl_msg_fatal("%s(%d): no service available", myname, __LINE__);

	/* Э鴦ʽ */
	acl_foreach(iter, services) {
		service = (SERVICE*) iter.data;

		if (strcasecmp(var_cfg_proto_name, service->name) == 0) {
			curr_service = service;
			__service_callback = service->module->mod_main;
			break;
		}
	}

	acl_fifo_free(services, NULL);

	if (curr_service == NULL)
		acl_msg_fatal("%s(%d): unknown protocol name(%s)",
			myname, __LINE__, var_cfg_proto_name);
	if (__service_callback == NULL)
		acl_msg_fatal("%s(%d): __service_callback null",
			myname, __LINE__);
	return (curr_service);
}

static void gc_timer(int event_type acl_unused, void *context)
{
	ACL_AIO *aio = (ACL_AIO *) context;

	acl_mem_slice_delay_destroy();
}

void service_init(ACL_AIO *aio, ACL_FIFO *modules)
{
	const char *myname = "service_init";
	ACL_ARGV *ip_argv = NULL;
	ACL_ITER iter;

	if (var_cfg_bind_ip_list && strcmp(var_cfg_bind_ip_list, "") != 0) {
		ip_argv = acl_argv_split(var_cfg_bind_ip_list, ",\t ");
	}

	if (var_cfg_aio_buf_size > 0)
		acl_aio_set_rbuf_size(aio, var_cfg_aio_buf_size);

	memset(&__dll_env, 0, sizeof(ACL_DLL_ENV));
	/*
	__dll_env.logfp = acl_log_fp();
	*/
	__dll_env.mem_slice = var_mem_slice;
	if (var_mem_slice) {
		/* 趨ʱʱ */
		acl_aio_request_timer(aio, gc_timer, aio, 2, 1);
	}

	if (__dll_env.mem_slice)
		acl_msg_info("%s(%d): use mem_slice", myname, __LINE__);
	else
		acl_msg_info("%s(%d): no use mem_slice", myname, __LINE__);

	if (var_cfg_debug_mem == 1) {
		acl_memory_debug_start();
		acl_memory_debug_stack(1);
	} else if (var_cfg_debug_mem == 2) {
		__dll_env.mmd = acl_debug_malloc_init(NULL, "service_log.txt");
	} else if (var_cfg_debug_mem == 3) {
		acl_memory_debug_start();
		acl_memory_debug_stack(1);
		__dll_env.mmd = acl_debug_malloc_init(NULL, "service_log.txt");
	}

#ifdef	JAWS_STATIC
	if (modules == NULL)
		acl_msg_fatal("%s(%d): modules null", myname, __LINE__);
	__service_modules = modules;
#else
	(void) modules;
	__service_modules = acl_fifo_new();
	service_load_all(__service_modules, var_cfg_service_dlnames);
#endif

	/* ʼмģ */
	acl_foreach(iter, __service_modules) {
		MODULE_SERVICE *module = (MODULE_SERVICE*) iter.data;
		module->mod_init(&__dll_env, var_cfg_service_cfgdir);
	}

	if (var_cfg_nthreads > 1) {
		int   i, event_mode = acl_aio_event_mode(aio);
		SERVICE *service;

		/* ʼ IPC ͨ */
		service_ipc_init(aio, var_cfg_nthreads);

		for (i = 0; i < var_cfg_nthreads; i++) {
			ACL_AIO *aio_thr;

			/* е߳ʵ첽 */
			aio_thr = acl_aio_create(event_mode);
			if (var_cfg_aio_buf_size > 0)
				acl_aio_set_rbuf_size(aio, var_cfg_aio_buf_size);
			service = service_create(aio_thr, __service_modules, ip_argv);
			service_ipc_add_service(service, __service_callback);
		}
	} else {
		__service = service_create(aio, __service_modules, ip_argv);
	}

	if (ip_argv)
		acl_argv_free(ip_argv);

	/* ڴնʱ */
	service_set_gctimer(aio, 10);
}

void service_exit(void)
{
	const char *myname = "service_exit";

	/* XXX: ʹڴƬʽʱжض̬صĿ core ļ:) */
	/*
	 * service_unload_all();
	 */
	acl_msg_info("%s(%d): jaws exit now", myname, __LINE__);
}

int service_main(ACL_SOCKET fd, ACL_AIO *aio)
{
	if (var_cfg_debug_mem)
		acl_msg_info("total alloc: %d", acl_mempool_total_allocated());

#ifdef ACL_UNIX
	acl_close_on_exec(fd, 1);
#endif
	if (var_cfg_nthreads > 1) {
		/* ߳ģʽ£ʵ̷߳µ */
		service_ipc_add(fd);
	} else {
		ACL_VSTREAM *vstream;
		ACL_ASTREAM *astream;

		/* ڵ߳ģʽ´첽 */
		vstream = acl_vstream_fdopen(fd, O_RDWR, var_cfg_aio_buf_size,
				0, ACL_VSTREAM_TYPE_SOCK);
		astream = acl_aio_open(aio, vstream);
		__service_callback(__service, astream);
	}
	return (0);
}
