/*
 * Copyright (c) 2004-2009, Luiz Otavio O Souza <loos.br@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

static const char rcsid[] = "$Id: p2p.c 112 2009-03-15 17:30:28Z loos-br $";

#include <stdio.h> /* XXX - printf */
#include <stdlib.h>
#include <string.h>

#include "io.h"
#include "msg.h"
#include "tree.h"
#include "return.h"
#include "msn-proxy.h"

typedef struct p2p__ {

    __int32_t           sessionid;
    __int32_t           id;
    __int64_t           offset;
    __int64_t           totalsize;
    __int32_t           msgsize;
    __int32_t           flags;
    __int32_t           ackid;
    __int32_t           ackuid;
    __int64_t           acksize;

    string		data;

    RB_ENTRY(p2p__)	entry;
} p2p_;

int
p2p_cmp(struct p2p__ *p2p1, struct p2p__ *p2p2) {
    return ((p2p1->sessionid + p2p1->id) - (p2p2->sessionid + p2p2->id));
}

RB_HEAD(p2p_tree_, p2p__) p2p_head_ = RB_INITIALIZER(&p2p_head_);
RB_GENERATE(p2p_tree_, p2p__, entry, p2p_cmp)

p2p_ *
p2p_alloc(void) {
 p2p_		*p2p;

    p2p = (p2p_ *)malloc(sizeof(p2p_));
    if (!p2p)
	return(p2p);

    memset(p2p, 0, sizeof(p2p_));
    return(p2p);
}

int
p2p_ack_packet(p2p_ *p2p) {
 __int64_t	acksize;
 p2p_		*p;

    RB_FOREACH(p, p2p_tree_, &p2p_head_) {

	acksize = p->totalsize + p->ackuid;
printf("acksize   [%lld] [%lld]\n", p2p->acksize, acksize); fflush(stdout);
io_printf(1, "acksize   [%d] [%d]\n", p2p->acksize, acksize);
io_printf(1, "msgsize   [%d] [%d]\n", p2p->msgsize, p->msgsize);
io_printf(1, "sessionid [%d] [%d]\n", p2p->sessionid, p->sessionid);
io_printf(1, "ackuid    [%d] [%d]\n", p2p->ackuid, p->ackid);
io_printf(1, "ackid     [%d] [%d]\n", p2p->ackid, p->id);
	if (	 p2p->acksize == acksize &&
		 p2p->sessionid == p->sessionid &&
		 (p2p->ackuid == 0 ||
		  p2p->ackuid == (p->ackid - p->offset)) &&
		 p2p->msgsize == 0 &&
		 p2p->ackid == p->id) {

	    RB_REMOVE(p2p_tree_, &p2p_head_, p);
	    free(p);

	    io_printf(1, "ack packet OK\n");
	    return(ROK);
	}
    }
    io_printf(1, "===> failed to ack packet\n");
    return(RFAIL);
}

int
msg_read_p2p_header(struct sb_ *sb, msg_ *msg) {
 header_	*header = (header_ *)0;
 p2p_		*p2p;
 p2p_		*p;
 int		state;

    if (msg->body.len < 48) {
	io_printf(1, "PANIC: SIZE < 48 BYTES [%d]\n", msg->body.len);
	return(RFAIL);
    }

    p2p = p2p_alloc();
    bcopy(msg->body.s, p2p, 48);
    msg->body.len -= 48;
    msg->body.s   += 48;

    io_printf(1, "sessionid: %d\n", p2p->sessionid);
    io_printf(1, "id       : %d\n", p2p->id);
    io_printf(1, "offset   : %d\n", p2p->offset);
    io_printf(1, "totalsize: %d\n", p2p->totalsize);
    io_printf(1, "msgsize  : %d\n", p2p->msgsize);
    io_printf(1, "flags    : %d\n", p2p->flags);
    io_printf(1, "ackid    : %d\n", p2p->ackid);
    io_printf(1, "ackuid   : %d\n", p2p->ackuid);
    io_printf(1, "acksize  : %d\n", p2p->acksize);

    RB_FOREACH(p, p2p_tree_, &p2p_head_) {

io_printf(1, "found:\n");
io_printf(1, "\tsessionid: %d\n", p->sessionid);
io_printf(1, "\tid       : %d\n", p->id);
io_printf(1, "\toffset   : %d\n", p->offset);
io_printf(1, "\ttotalsize: %d\n", p->totalsize);
io_printf(1, "\tmsgsize  : %d\n", p->msgsize);
io_printf(1, "\tflags    : %d\n", p->flags);
io_printf(1, "\tackid    : %d\n", p->ackid);
io_printf(1, "\tackuid   : %d\n", p->ackuid);
io_printf(1, "\tacksize  : %d\n", p->acksize);
    }

    /* check for ack packets */
    if ((p2p->flags & P2P_ACK) && p2p->acksize > 0) {
	io_printf(1, "===> ack packetd\n");
	if (p2p_ack_packet(p2p) == RFAIL)
	    return(RFAIL);
	//type = ACK; return(type);
	//or return(ACK);
	return(ROK);
    }

    if (p2p->flags & P2P_FILE) {
	p = RB_FIND(p2p_tree_, &p2p_head_, p2p);
	if (p) {
	    if (! p->flags & P2P_FILE ||
		  p->totalsize != p2p->totalsize) {
		io_printf(1, "===> fail to update packet information\n");
		return(RFAIL);
	    }
	    io_printf(1, "===> ready to update\n");
	    RB_REMOVE(p2p_tree_, &p2p_head_, p);
	}
    }

    /* add p2p session */
    p = RB_INSERT(p2p_tree_, &p2p_head_, p2p);
    if (p) {
	io_printf(1, "===> already here 2 ?\n");
	return(RFAIL);
    }
    io_printf(1, "===> rb_insert ok\n");

io_printf(1, "body: [%s]\n", &msg->body);

    state = HEADER;
    while (msg->body.len > 0) {

	if (!header)
	    header = header_alloc();
	if (!header)
	    return(RFAIL);

	/* ignore '\r' */
	if (*msg->body.s == '\r') {
	    msg->body.len -= 1;
	    msg->body.s   += 1;
	    continue;
	}

	/* start of value */
	if (*msg->body.s == ':') {
	    state = START_VALUE;
	    msg->body.len -= 1;
	    msg->body.s   += 1;
	    continue;
	}

	if (*msg->body.s == '\n' && state == HEADER) {
	    state = BODY;
	    break;
	}

	if (*msg->body.s == '\n') {
io_printf(1, "HEADER: [%s]\n", &header->header);
io_printf(1, "VALUE: [%s]\n", &header->value);
//            msg_add_header(msg, header);
            header = (header_ *)0;
            state = HEADER;

            msg->body.len -= 1;
            msg->body.s   += 1;
            continue;
	}

	if (state == START_VALUE) {
            if (*msg->body.s == ' ' || *msg->body.s == '\t') {
                msg->body.len -= 1;
                msg->body.s   += 1;
                continue;
            } else
                state = VALUE;
	}

	if (state == HEADER) {
	    if (str_cat(&header->header, msg->body.s, 1) == 0)
                return(RFAIL);
	} else if (state == VALUE) {
           if (str_cat(&header->value, msg->body.s, 1) == 0)
                return(RFAIL);
	}
	msg->body.len -= 1;
	msg->body.s   += 1;
    }

    if (state != BODY) {
	io_printf(1, "PANIC: STATE != BODY\n");
	return(RFAIL);
    }

io_printf(1, "resta: [%d]\n", msg->body.len);

    return(RFAIL);
}

