/*
 * rpc_request.c
 *
 *  Created on: 2011-3-21
 *      Author: yanghu
 */

#include "rpc_response.h"
#include "rpc_conn.h"
#include "rpc_array.h"
#include "rpc_util.h"
#include <assert.h>

static pthread_mutex_t freelock = PTHREAD_MUTEX_INITIALIZER;

static rpc_array *rpc_response_freelist = NULL;

rpc_response* rpc_response_new() {
	if (rpc_response_freelist == NULL) {
		pthread_mutex_lock(&freelock);
		if (rpc_response_freelist == NULL) {
			rpc_response_freelist = rpc_array_new();
		}
		pthread_mutex_unlock(&freelock);
	}
	//rpc_response *rsp = g_new(rpc_response,1);
	rpc_response *rsp = (rpc_response*) rpc_array_get(rpc_response_freelist);
	if (rsp == NULL) {
		rsp = rpc_new(rpc_response,1);
	}
	return rsp;
}

void rpc_response_free(rpc_response *rsp) {
	memset(rsp, 0, sizeof(rpc_response));
	if (!rpc_array_add(rpc_response_freelist, rsp)) {
		rpc_free(rsp);
	}
}

rpc_response* rpc_response_copy_head(rpc_response *rsp) {
	rpc_response *rsp_new = rpc_response_new();
	rsp_new->code = rsp->code;
	rsp_new->seq = rsp->seq;
	rsp_new->data = rsp->data;
	rsp_new->output_len = rsp->output_len;
	rsp_new->phrase = strdup(rsp->phrase);
	return rsp_new;
}

rpc_parse_result rpc_response_parse(rpc_conn *c, rpc_response **rsp) {
	//request line
	char *el, *cont;
	int avail, num;
	rpc_response *response = NULL;
	el = memchr(c->rcurr, '\n', c->rbytes);
	avail = c->rbytes;
	if (el) {
		cont = el + 1;
		avail -= (cont - c->rcurr);
		//windows newline separate is \r\n
		if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
			el--;
		}
		*el = '\0';
		token_t tokens[3];
		int n = tokenize_command(c->rcurr, tokens, 3);
		if (n != 3 && strcmp(tokens[0].value, RPC_VERSION) != 0) {
			fprintf(stderr, "status line begin error!\n");
			return RPC_Parse_Error;
		}
		response = rpc_response_new();
		response->code = (int) strtol(tokens[1].value, NULL, 10);
		response->phrase = tokens[2].value;
		c->rcurr = cont;
		c->rbytes = avail;
	} else {
		fprintf(stderr, "not status line error!\n");
		return RPC_Parse_Error;
	}
	//seq
	el = memchr(c->rcurr, '\n', avail);
	if (el) {
		cont = el + 1;
		avail -= (cont - c->rcurr);
		if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
			el--;
		}
		*el = '\0';
		num = sscanf(c->rcurr, "seq:%d", &response->seq);
		c->rcurr = cont;
		c->rbytes = avail;
		if (num != 1) {
			rpc_response_free(response);
			fprintf(stderr, "seq parse error!\n");
			return RPC_Parse_Error;
		}
	} else {
		rpc_response_free(response);
		fprintf(stderr, "not seq line error!\n");
		return RPC_Parse_Error;
	}
	//body len
	el = memchr(c->rcurr, '\n', avail);
	if (el) {
		cont = el + 1;
		avail -= (cont - c->rcurr);
		if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
			el--;
		}
		*el = '\0';
		num = sscanf(c->rcurr, "body-len:%d", &response->output_len);
		c->rcurr = cont;
		c->rbytes = avail;
		if (num != 1) {
			rpc_response_free(response);
			fprintf(stderr, "body-len line parse error!\n");
			return RPC_Parse_Error;
		}
	} else {
		rpc_response_free(response);
		fprintf(stderr, "not body-len line error!\n");
		return RPC_Parse_Error;
	}
	//new line
	el = memchr(c->rcurr, '\n', avail);
	if (el) {
		cont = el + 1;
		avail -= (cont - c->rcurr);
		if ((el - c->rcurr == 0) || ((el - c->rcurr) == 1 && *(el - 1) == '\r')) {
			if (avail >= response->output_len) {
				response->output = cont;
				c->rcurr = cont + response->output_len;
				c->rbytes = avail - response->output_len;
				*rsp = response;
				return RPC_Parse_OK;
			} else {
				c->rcurr = cont;
				c->rbytes = avail;
				*rsp = response;
				return RPC_Parse_NeedData;
			}
		}
	}
	fprintf(stderr, "body error!\n");
	rpc_response_free(response);
	return RPC_Parse_Error;
}