/* objreq.c
 * Object Requester
 * (c) 2002 Mikulas Patocka
 * This file is a part of the Links program, released under GPL.
 */

#include "links.h"

void objreq_end(struct status *, struct object_request *);
void object_timer(struct object_request *);

/* prev_url is a pointer to previous url or NULL */
/* prev_url will NOT be deallocated */
void request_object(struct terminal *term, unsigned char *url, unsigned char *prev_url, int pri, int cache, void (*upcall)(struct object_request *, void *), void *data, struct object_request **rqp)
{
	struct object_request *rq;
	if (!(rq = mem_calloc(sizeof(struct object_request)))) return;
	rq->state = O_WAITING;
	rq->refcount = 1;
	rq->term = term ? term->count : 0;
	rq->stat.end = (void (*)(struct status *, void *))objreq_end;
	rq->stat.data = rq;
	if (!(rq->orig_url = stracpy(url))) {
		mem_free(rq);
		return;
	}
	if (!(rq->url = stracpy(url))) {
		mem_free(rq->orig_url);
		mem_free(rq);
		return;
	}
	rq->pri = pri;
	rq->cache = cache;
	rq->upcall = upcall;
	rq->data = data;
	rq->timer = -1;
	rq->z = get_time() - STAT_UPDATE_MAX;
	rq->last_update = rq->z;
	rq->last_bytes = 0;
	if (rq->prev_url)mem_free(rq->prev_url);
	rq->prev_url=stracpy(prev_url);
	if (rqp) *rqp = rq;
	load_url(url, prev_url, &rq->stat, pri, cache);
}

void objreq_end(struct status *stat, struct object_request *rq)
{
	if (stat->state < 0) {
		if (stat->ce && stat->ce->redirect && rq->state == O_WAITING) {
			if (rq->redirect_cnt++ < MAX_REDIRECTS) {
				unsigned char *u, *p;
				change_connection(stat, NULL, PRI_CANCEL);
				u = join_urls(rq->url, stat->ce->redirect);
				if (!http_bugs.bug_302_redirect && !stat->ce->redirect_get && (p = strchr(u, POST_CHAR))) add_to_strn(&u, p);
				mem_free(rq->url);
				rq->url = u;
				load_url(u, rq->prev_url, &rq->stat, rq->pri, rq->cache);
				return;
			} else {
				rq->stat.state = S_CYCLIC_REDIRECT;
			}
		}
	}
	if (stat->ce && !stat->ce->redirect) {
		rq->state = O_LOADING;
		if (!rq->ce) (rq->ce = stat->ce)->refcount++;
	}
	if (rq->timer != -1) kill_timer(rq->timer);
	rq->timer = install_timer(0, (void (*)(void *))object_timer, rq);
}

void object_timer(struct object_request *rq)
{
	int last = rq->last_bytes;
	if (rq->ce) rq->last_bytes = rq->ce->length;
	rq->timer = -1;
	if (rq->stat.state < 0 && (!rq->stat.ce || !rq->stat.ce->redirect || rq->stat.state == S_CYCLIC_REDIRECT)) {
		if (rq->stat.ce && rq->stat.state != S_CYCLIC_REDIRECT) {
			rq->state = rq->stat.state != S_OK ? O_INCOMPLETE : O_OK;
			/*(rq->ce = rq->stat.ce)->refcount++;*/
		} else rq->state = O_FAILED;
	}
	if (rq->stat.state != S_TRANS) {
		rq->last_update = rq->z;
		if (rq->upcall) rq->upcall(rq, rq->data);
	} else {
		ttime ct = get_time();
		ttime t = ct - rq->last_update;
		rq->timer = install_timer(STAT_UPDATE_MIN, (void (*)(void *))object_timer, rq);
		if (t >= STAT_UPDATE_MAX || (t >= STAT_UPDATE_MIN && rq->ce && rq->last_bytes > last)) {
			rq->last_update = ct;
			if (rq->upcall) rq->upcall(rq, rq->data);
		}
	}
}

void release_object_get_stat(struct object_request **rqq, struct status *news, int pri)
{
	struct object_request *rq = *rqq;
	if (!rq) return;
	*rqq = NULL;
	if (--rq->refcount) return;
	change_connection(&rq->stat, news, pri);
	if (rq->timer != -1) kill_timer(rq->timer);
	if (rq->ce) rq->ce->refcount--;
	mem_free(rq->orig_url);
	mem_free(rq->url);
	if (rq->prev_url)mem_free(rq->prev_url);
	mem_free(rq);
}

void release_object(struct object_request **rqq)
{
	release_object_get_stat(rqq, NULL, PRI_CANCEL);
}

void detach_object_connection(struct object_request *rq, int pos)
{
	if (rq->state == O_WAITING || rq->state == O_FAILED) {
		internal("detach_object_connection: no data received");
		return;
	}
	if (rq->refcount == 1) detach_connection(&rq->stat, pos);
}

void clone_object(struct object_request *rq, struct object_request **rqq)
{
	(*rqq = rq)->refcount++;
}
