#include "stdafx.h"
#include "service_main.h"

static int respond_data_callback(ACL_ASTREAM *astream acl_unused,
	void *context, const char *data, int len)
{
	const char *myname = "respond_data_callback";
	SERVICE_CTX *respond_ctx = (SERVICE_CTX*) context;
	SERVICE_CTX *request_ctx;
	struct iovec chunks[2];
	unsigned short n;
	int   dlen;
	char  buf[MAX_BUF];

	request_ctx = service_ctx_find(respond_ctx->service,
		SERVICE_CTX_TCP_REQUEST, respond_ctx->id);
	if (request_ctx == NULL) {
		acl_msg_error("%s(%d): request client(%d) exit now",
			myname, __LINE__, respond_ctx->id);
		return (-1);
	}

	n = htons(len);
	dlen = len > MAX_BUF ? MAX_BUF : len;
	memcpy(buf, data, dlen);
	chunks[0].iov_base = &n;
	chunks[0].iov_len = 2;
	chunks[1].iov_base = buf;
	chunks[1].iov_len = dlen;
	acl_aio_writev(request_ctx->stream, chunks, 2);
	acl_aio_iocp_close(request_ctx->stream);
	return (-1);
}

static int respond_len_callback(ACL_ASTREAM *astream, void *context,
	char *data, int len acl_unused)
{
	const char *myname = "respond_len_callback";
	unsigned short n;

	n = ntohs(*(const unsigned short *) data);
	if (n <= 0) {
		acl_msg_error("%s(%d): n(%d) invalid", myname, __LINE__, n);
		return (-1);
	}
	if (n > MAX_BUF) {
		acl_msg_error("%s(%d): n(%d) too long", myname, __LINE__, n);
		return (-1);
	}
	acl_aio_ctl(astream,
		ACL_AIO_CTL_READ_HOOK_ADD, respond_data_callback, context,
		ACL_AIO_CTL_END);
	acl_aio_readn(astream, n);
	return (0);
}

static int write_request_callback(ACL_ASTREAM *astream, void *context)
{
	//SERVICE_CTX *ctx = (SERVICE_CTX*) context;
	acl_aio_ctl(astream,
		ACL_AIO_CTL_READ_HOOK_ADD, respond_len_callback, context,
		ACL_AIO_CTL_END);
	acl_aio_readn(astream, 2);
	return (0);
}

static int respond_connect_callback(ACL_ASTREAM *astream, void *context)
{
	SERVICE_CTX *ctx = (SERVICE_CTX*) context;
	unsigned short len;
	struct iovec chunks[2];

	acl_aio_ctl(astream,
		ACL_AIO_CTL_WRITE_HOOK_ADD, write_request_callback, context,
		ACL_AIO_CTL_END);
	len = htons(ctx->request_len);
	chunks[0].iov_base = &len;
	chunks[0].iov_len = 2;
	chunks[1].iov_base = ctx->request_buf;
	chunks[1].iov_len = ctx->request_len;
	acl_aio_writev(astream, chunks, 2);
	return (0);
}

static int respond_close_callback(ACL_ASTREAM *astream acl_unused, void *context)
{
	SERVICE_CTX *ctx = (SERVICE_CTX*) context;

	service_ctx_free(ctx);
	return (-1);
}

static int request_data_callback(ACL_ASTREAM *astream acl_unused, void *context,
	char *data, int len)
{
	const char *myname = "request_data_callback";
	SERVICE_CTX *request_ctx = (SERVICE_CTX*) context, *respond_ctx;
	SERVICE *service = request_ctx->service;
	ACL_ASTREAM *server;

	server = acl_aio_connect(service->aio, service->dns_addr,
		service->conn_timeout);
	if (server == NULL) {
		acl_msg_error("%s(%d): connect dns(%s) error",
			myname, __LINE__, service->dns_addr);
		return (-1);
	}

	respond_ctx = service_ctx_new(service, server,
		SERVICE_CTX_TCP_RESPOND, request_ctx->id);  // 与请求端共享同一ID
	memcpy(respond_ctx->request_buf, data, len);
	respond_ctx->request_len = len;
	acl_aio_ctl(server,
		ACL_AIO_CTL_CONNECT_HOOK_ADD, respond_connect_callback, respond_ctx,
		ACL_AIO_CTL_CLOSE_HOOK_ADD, respond_close_callback, respond_ctx,
		ACL_AIO_CTL_CTX, respond_ctx,
		ACL_AIO_CTL_END);
	return (0);
}

static int request_len_callback(ACL_ASTREAM *astream, void *context,
	char *data, int len acl_unused)
{
	const char *myname = "request_len_callback";
	//SERVICE_CTX *ctx = (SERVICE_CTX*) context;
	unsigned short n;

	n = ntohs(*(const unsigned short *) data);
	if (n <= 0) {
		acl_msg_error("%s(%d): n(%d) invalid", myname, __LINE__, n);
		return (-1);
	}
	acl_aio_ctl(astream,
		ACL_AIO_CTL_READ_HOOK_ADD, request_data_callback, context,
		ACL_AIO_CTL_END);
	acl_aio_readn(astream, n);
	return (0);
}

static int request_close_callback(ACL_ASTREAM *astream acl_unused, void *context)
{
	SERVICE_CTX *ctx = (SERVICE_CTX*) context;

	service_ctx_free(ctx);
	return (-1);
}

void service_tcp_main(ACL_ASTREAM *client, SERVICE *service)
{
	//const char *myname = "service_tcp_main";
	SERVICE_CTX *ctx;

	ctx = service_ctx_new(service, client, SERVICE_CTX_TCP_REQUEST,
		service->curr_id++);
	acl_aio_ctl(client,
		ACL_AIO_CTL_READ_HOOK_ADD, request_len_callback, ctx,
		ACL_AIO_CTL_CLOSE_HOOK_ADD, request_close_callback, ctx,
		ACL_AIO_CTL_TIMEOUT, service->rw_timeout,
		ACL_AIO_CTL_CTX, ctx,
		ACL_AIO_CTL_END);
	acl_aio_readn(client, 2);  // 读取两个字节
}
