#include "uwsgi.h"

extern struct uwsgi_server uwsgi;

struct uwsgi_buffer *uwsgi_buffer_new(size_t len) {
#ifdef UWSGI_DEBUG_BUFFER
	uwsgi_log("[uwsgi-buffer] allocating a new buffer of %llu\n", (unsigned long long) len);
#endif
	struct uwsgi_buffer *ub = uwsgi_calloc(sizeof(struct uwsgi_buffer));

	if (len) {
		ub->buf = uwsgi_malloc(len);
		ub->len = len;
	}
	return ub;

}

int uwsgi_buffer_fix(struct uwsgi_buffer *ub, size_t len) {
	if (ub->limit > 0 && len > ub->limit)
		return -1;
	if (ub->len < len) {
		char *new_buf = realloc(ub->buf, len);
		if (!new_buf) {
			uwsgi_error("uwsgi_buffer_fix()");
			return -1;
		}
		ub->buf = new_buf;
		ub->len = len;
	}
	return 0;
}

int uwsgi_buffer_ensure(struct uwsgi_buffer *ub, size_t len) {
	size_t remains = ub->len - ub->pos;
	if (remains < len) {
		size_t new_len = ub->len + (len - remains);
		if (ub->limit > 0 && new_len > ub->limit) {
			new_len = ub->limit;
			if (new_len == ub->len)
				return -1;
		}
		char *new_buf = realloc(ub->buf, new_len);
		if (!new_buf) {
			uwsgi_error("uwsgi_buffer_ensure()");
			return -1;
		}
		ub->buf = new_buf;
		ub->len = new_len;
	}
	return 0;
}

int uwsgi_buffer_insert(struct uwsgi_buffer *ub, size_t pos, char *buf, size_t len) {
	size_t to_move = (ub->pos-pos);
	if (uwsgi_buffer_ensure(ub, len)) return -1;
	memmove(ub->buf+pos+len, ub->buf+pos, to_move);
	memcpy(ub->buf+pos, buf, len);
	ub->pos += len;
	return 0;
}

int uwsgi_buffer_insert_chunked(struct uwsgi_buffer *ub, size_t pos, size_t len) {
	// 0xFFFFFFFFFFFFFFFF\r\n\0
	char chunked[19];
	int ret = snprintf(chunked, 19, "%X\r\n", (unsigned int) len);
        if (ret <= 0 || ret >= 19) {
                return -1;
        }
	return uwsgi_buffer_insert(ub, pos, chunked, ret);
}

int uwsgi_buffer_append_chunked(struct uwsgi_buffer *ub, size_t len) {
        // 0xFFFFFFFFFFFFFFFF\r\n\0
        char chunked[19];
        int ret = snprintf(chunked, 19, "%X\r\n", (unsigned int) len);
        if (ret <= 0 || ret >= 19) {
                return -1;
        }
        return uwsgi_buffer_append(ub, chunked, ret);
}


int uwsgi_buffer_decapitate(struct uwsgi_buffer *ub, size_t len) {
	if (len > ub->pos) return -1;
	memmove(ub->buf, ub->buf + len, ub->pos-len);
	ub->pos = ub->pos-len;
	return 0;
}

int uwsgi_buffer_byte(struct uwsgi_buffer *ub, char byte) {
	return uwsgi_buffer_append(ub, &byte, 1);
}

int uwsgi_buffer_u8(struct uwsgi_buffer *ub, uint8_t u8) {
	return uwsgi_buffer_append(ub, (char *) &u8, 1);
}

int uwsgi_buffer_append(struct uwsgi_buffer *ub, char *buf, size_t len) {

	size_t remains = ub->len - ub->pos;

	if (len > remains) {
		size_t chunk_size = UMAX(len, (size_t) uwsgi.page_size);
		if (ub->limit > 0 && ub->len + chunk_size > ub->limit) {
			// retry with another minimal size
			if (len < (size_t) uwsgi.page_size) {
				chunk_size = len;
			}
			if (ub->len + chunk_size > ub->limit)
				return -1;
		}
		char *new_buf = realloc(ub->buf, ub->len + chunk_size);
		if (!new_buf) {
			uwsgi_error("uwsgi_buffer_append()");
			return -1;
		}
		ub->buf = new_buf;
		ub->len += chunk_size;
	}

	memcpy(ub->buf + ub->pos, buf, len);
	ub->pos += len;
	return 0;
}

int uwsgi_buffer_append_json(struct uwsgi_buffer *ub, char *buf, size_t len) {
	// need to escape \ and "
	size_t i;
	for(i=0;i<len;i++) {
		if (buf[i] == '"') {
			if (uwsgi_buffer_append(ub, "\\\"", 2)) return -1;
		}
		else if (buf[i] == '\\') {
			if (uwsgi_buffer_append(ub, "\\\\", 2)) return -1;
		}
		else {
			if (uwsgi_buffer_append(ub, buf+i, 1)) return -1;
		}
	}
	return 0;
}

int uwsgi_buffer_u16le(struct uwsgi_buffer *ub, uint16_t num) {
	uint8_t buf[2];
	buf[0] = (uint8_t) (num & 0xff);
        buf[1] = (uint8_t) ((num >> 8) & 0xff);
	return uwsgi_buffer_append(ub, (char *) buf, 2);
}

int uwsgi_buffer_u16be(struct uwsgi_buffer *ub, uint16_t num) {
        uint8_t buf[2];
        buf[1] = (uint8_t) (num & 0xff);
        buf[0] = (uint8_t) ((num >> 8) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 2);
}

int uwsgi_buffer_u24be(struct uwsgi_buffer *ub, uint32_t num) {
        uint8_t buf[3];
        buf[2] = (uint8_t) (num & 0xff);
        buf[1] = (uint8_t) ((num >> 8) & 0xff);
        buf[0] = (uint8_t) ((num >> 16) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 3);
}

int uwsgi_buffer_u32be(struct uwsgi_buffer *ub, uint32_t num) {
        uint8_t buf[4];
        buf[3] = (uint8_t) (num & 0xff);
        buf[2] = (uint8_t) ((num >> 8) & 0xff);
        buf[1] = (uint8_t) ((num >> 16) & 0xff);
        buf[0] = (uint8_t) ((num >> 24) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 4);
}

int uwsgi_buffer_f32be(struct uwsgi_buffer *ub, float num) {
        uint8_t buf[4];
	uint32_t *pnum = (uint32_t *) &num;
        buf[3] = (uint8_t) (*pnum & 0xff);
        buf[2] = (uint8_t) ((*pnum >> 8) & 0xff);
        buf[1] = (uint8_t) ((*pnum >> 16) & 0xff);
        buf[0] = (uint8_t) ((*pnum >> 24) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 4);
}

int uwsgi_buffer_u32le(struct uwsgi_buffer *ub, uint32_t num) {
        uint8_t buf[4];
        buf[0] = (uint8_t) (num & 0xff);
        buf[1] = (uint8_t) ((num >> 8) & 0xff);
        buf[2] = (uint8_t) ((num >> 16) & 0xff);
        buf[3] = (uint8_t) ((num >> 24) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 4);
}

int uwsgi_buffer_u64be(struct uwsgi_buffer *ub, uint64_t num) {
        uint8_t buf[8];
        buf[7] = (uint8_t) (num & 0xff);
        buf[6] = (uint8_t) ((num >> 8) & 0xff);
        buf[5] = (uint8_t) ((num >> 16) & 0xff);
        buf[4] = (uint8_t) ((num >> 24) & 0xff);
        buf[3] = (uint8_t) ((num >> 32) & 0xff);
        buf[2] = (uint8_t) ((num >> 40) & 0xff);
        buf[1] = (uint8_t) ((num >> 48) & 0xff);
        buf[0] = (uint8_t) ((num >> 56) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 8);
}

int uwsgi_buffer_u64le(struct uwsgi_buffer *ub, uint64_t num) {
	uint8_t buf[8];
        buf[0] = (uint8_t) (num & 0xff);
        buf[1] = (uint8_t) ((num >> 8) & 0xff);
        buf[2] = (uint8_t) ((num >> 16) & 0xff);
        buf[3] = (uint8_t) ((num >> 24) & 0xff);
	buf[4] = (uint8_t) ((num >> 32) & 0xff);
        buf[5] = (uint8_t) ((num >> 40) & 0xff);
        buf[6] = (uint8_t) ((num >> 48) & 0xff);
        buf[7] = (uint8_t) ((num >> 56) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 8);
}


int uwsgi_buffer_f64be(struct uwsgi_buffer *ub, double num) {
        uint8_t buf[8];
	uint64_t *pnum = (uint64_t *) &num;
        buf[7] = (uint8_t) (*pnum & 0xff);
        buf[6] = (uint8_t) ((*pnum >> 8) & 0xff);
        buf[5] = (uint8_t) ((*pnum >> 16) & 0xff);
        buf[4] = (uint8_t) ((*pnum >> 24) & 0xff);
        buf[3] = (uint8_t) ((*pnum >> 32) & 0xff);
        buf[2] = (uint8_t) ((*pnum >> 40) & 0xff);
        buf[1] = (uint8_t) ((*pnum >> 48) & 0xff);
        buf[0] = (uint8_t) ((*pnum >> 56) & 0xff);
        return uwsgi_buffer_append(ub, (char *) buf, 8);
}

int uwsgi_buffer_append_ipv4(struct uwsgi_buffer *ub, void *addr) {
	char ip[INET_ADDRSTRLEN];
	if (!inet_ntop(AF_INET, addr, ip, INET_ADDRSTRLEN)) {
		uwsgi_error("uwsgi_buffer_append_ipv4() -> inet_ntop()");
		return -1;
	}
	return uwsgi_buffer_append(ub, ip, strlen(ip));
}


int uwsgi_buffer_num64(struct uwsgi_buffer *ub, int64_t num) {
	char buf[sizeof(UMAX64_STR)+1];
	int ret = snprintf(buf, sizeof(UMAX64_STR)+1, "%lld", (long long) num);
	if (ret <= 0 || ret >= (int) (sizeof(UMAX64_STR)+1)) {
		return -1;
	}
	return uwsgi_buffer_append(ub, buf, ret);
}

int uwsgi_buffer_append_keyval(struct uwsgi_buffer *ub, char *key, uint16_t keylen, char *val, uint16_t vallen) {
	if (uwsgi_buffer_u16le(ub, keylen)) return -1;
	if (uwsgi_buffer_append(ub, key, keylen)) return -1;
	if (uwsgi_buffer_u16le(ub, vallen)) return -1;
	return uwsgi_buffer_append(ub, val, vallen);
}

int uwsgi_buffer_append_keyval32(struct uwsgi_buffer *ub, char *key, uint32_t keylen, char *val, uint32_t vallen) {
        if (uwsgi_buffer_u32be(ub, keylen)) return -1;
        if (uwsgi_buffer_append(ub, key, keylen)) return -1;
        if (uwsgi_buffer_u32be(ub, vallen)) return -1;
        return uwsgi_buffer_append(ub, val, vallen);
}

int uwsgi_buffer_append_keynum(struct uwsgi_buffer *ub, char *key, uint16_t keylen, int64_t num) {
	char buf[sizeof(UMAX64_STR)+1];
        int ret = snprintf(buf, (sizeof(UMAX64_STR)+1), "%lld", (long long) num);
        if (ret <= 0 || ret >= (int) (sizeof(UMAX64_STR)+1)) {
                return -1;
        }
	if (uwsgi_buffer_u16le(ub, keylen)) return -1;
	if (uwsgi_buffer_append(ub, key, keylen)) return -1;
	if (uwsgi_buffer_u16le(ub, ret)) return -1;
	return uwsgi_buffer_append(ub, buf, ret);
}

int uwsgi_buffer_append_valnum(struct uwsgi_buffer *ub, int64_t num) {
        char buf[sizeof(UMAX64_STR)+1];
        int ret = snprintf(buf, (sizeof(UMAX64_STR)+1), "%lld", (long long) num);
        if (ret <= 0 || ret >= (int) (sizeof(UMAX64_STR)+1)) {
                return -1;
        }
        if (uwsgi_buffer_u16le(ub, ret)) return -1;
        return uwsgi_buffer_append(ub, buf, ret);
}


int uwsgi_buffer_append_keyipv4(struct uwsgi_buffer *ub, char *key, uint16_t keylen, void *addr) {
        if (uwsgi_buffer_u16le(ub, keylen)) return -1;
        if (uwsgi_buffer_append(ub, key, keylen)) return -1;
        if (uwsgi_buffer_u16le(ub, 15)) return -1;
	char *ptr = ub->buf + (ub->pos - 2);
        if (uwsgi_buffer_append_ipv4(ub, addr)) return -1;
	// fix the size
	*ptr = ((ub->buf+ub->pos) - (ptr+2));
	return 0;
}

int uwsgi_buffer_append_base64(struct uwsgi_buffer *ub, char *s, size_t len) {
	size_t b64_len = 0;
	char *b64 = uwsgi_base64_encode(s, len, &b64_len);
	if (!b64) return -1;
	int ret = uwsgi_buffer_append(ub, b64, b64_len);
	free(b64);
	return ret;
}


void uwsgi_buffer_destroy(struct uwsgi_buffer *ub) {
#ifdef UWSGI_DEBUG_BUFFER
	uwsgi_log("[uwsgi-buffer] destroying buffer of %llu bytes\n", (unsigned long long) ub->len);
	if (ub->freed) {
		uwsgi_log("[uwsgi-buffer][BUG] buffer at %p already destroyed !!!\n", ub);
	}
	ub->freed = 1;
#endif
	if (ub->buf)
		free(ub->buf);
	free(ub);
}

ssize_t uwsgi_buffer_write_simple(struct wsgi_request *wsgi_req, struct uwsgi_buffer *ub) {
	size_t remains = ub->pos;
	while(remains) {
		ssize_t len = write(wsgi_req->fd, ub->buf + (ub->pos - remains), remains);
		if (len <= 0) {
			return len;
		}
		remains -= len;
	}

	return ub->pos;
}

int uwsgi_buffer_send(struct uwsgi_buffer *ub, int fd) {
	size_t remains = ub->pos;
	char *ptr = ub->buf;

	while (remains > 0) {
		int ret = uwsgi_waitfd_write(fd, uwsgi.socket_timeout);
		if (ret > 0) {
			ssize_t len = write(fd, ptr, remains);
			if (len > 0) {
				ptr += len;
				remains -= len;
			}
			else if (len == 0) {
				return -1;
			}
			else {
				uwsgi_error("uwsgi_buffer_send()");
				return -1;
			}
		}
		else if (ret == 0) {
			uwsgi_log("timeout while sending buffer !!!\n");
			return -1;
		}
		else {
			return -1;
		}
	}

	return 0;
}

int uwsgi_buffer_set_uh(struct uwsgi_buffer *ub, uint8_t modifier1, uint8_t modifier2) {
	if (ub->pos < 4) return -1;
	ub->buf[0] = modifier1;
	ub->buf[1] = (uint8_t) ((ub->pos - 4) & 0xff);
	ub->buf[2] = (uint8_t) (((ub->pos - 4) >> 8) & 0xff);
	ub->buf[3] = modifier2;
	return 0;
}

struct uwsgi_buffer *uwsgi_buffer_from_file(char *filename) {
	struct stat st;
        int fd = open(filename, O_RDONLY);
        if (fd < 0) {
		return NULL;
        }

        if (fstat(fd, &st)) {
                close(fd);
		return NULL;
        }

        struct uwsgi_buffer *ub = uwsgi_buffer_new(st.st_size);

        ssize_t len = read(fd, ub->buf, st.st_size);
	close(fd);
        if (len != st.st_size) {
		uwsgi_buffer_destroy(ub);
		return NULL;
        }

	ub->pos = len;
	return ub;
}

void uwsgi_buffer_map(struct uwsgi_buffer *ub, char *buf, size_t len) {
	if (ub->buf) {
		free(ub->buf);
	}
	ub->buf = buf;
	ub->pos = len;
	ub->len = len;
}
