/*
 * qstat 2.14
 * by Steve Jankowski
 *
 * qserver functions
 * Copyright 2004 Ludwig Nussel
 *
 * Licensed under the Artistic License, see LICENSE.txt for license terms
 */

#include "qstat.h"
#include "qserver.h"
#include "debug.h"

#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

// TODO: get rid of this and use send_packet instead, remove n_requests hack from a2s
query_status_t qserver_send_initial(struct qserver* server, const char* data, size_t len)
{
    int status = INPROGRESS;

    if( data )
    {
		int ret;
		debug( 2, "[%s] send", server->type->type_prefix );
		if( 4 <= get_debug_level() )
		{
			output_packet( server, data, len, 1 );
		}

		if ( server->flags & FLAG_BROADCAST)
		{
			ret = send_broadcast(server, data, len);
		}
		else
		{
			ret = send( server->fd, data, len, 0);
		}

		if ( ret == SOCKET_ERROR)
		{
			send_error( server, ret );
			status = SYS_ERROR;
		}
    }

    if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST)
    {
		gettimeofday( &server->packet_time1, NULL);
    }
    else
    {
		server->n_retries++;
    }

    server->retry1--;
    server->n_packets++;

    return status;
}

query_status_t qserver_send(struct qserver* server, const char* data, size_t len)
{
    int status = INPROGRESS;

    if(data)
    {
		int ret;
		if ( server->flags & FLAG_BROADCAST)
		{
			ret = send_broadcast(server, data, len);
		}
		else
		{
			ret = send( server->fd, data, len, 0);
		}

		if ( ret == SOCKET_ERROR)
		{
			send_error( server, ret );
			status = SYS_ERROR;
		}
    }

    server->retry1 = n_retries-1;
    gettimeofday( &server->packet_time1, NULL);
    server->n_requests++;
    server->n_packets++;

    return status;

}

int send_broadcast( struct qserver *server, const char *pkt, size_t pktlen)
{
    struct sockaddr_in addr;
    addr.sin_family= AF_INET;
    if ( no_port_offset || server->flags & TF_NO_PORT_OFFSET)
	{
        addr.sin_port= htons(server->port);
	}
    else
	{
        addr.sin_port= htons((unsigned short)( server->port + server->type->port_offset ));
	}
    addr.sin_addr.s_addr= server->ipaddr;
    memset( &(addr.sin_zero), 0, sizeof(addr.sin_zero));

    return sendto( server->fd, (const char*) pkt, pktlen, 0, (struct sockaddr *) &addr, sizeof(addr));
}

int register_send( struct qserver *server )
{
    if ( server->retry1 == n_retries || server->flags & FLAG_BROADCAST )
	{
		server->n_requests++;
    }
    else
	{
		server->n_retries++;
	}

	// New request so reset the sent time. This ensures
	// that we record an accurate ping time even on retry
	gettimeofday( &server->packet_time1, NULL);
    server->retry1--;
    server->n_packets++;

	return INPROGRESS;
}

query_status_t send_packet( struct qserver* server, const char* data, size_t len )
{
	debug( 2, "[%s] send", server->type->type_prefix );
	if( 4 <= get_debug_level() )
	{
		output_packet( server, data, len, 1 );
	}

    if( data )
    {
		int ret;
		if ( server->flags & FLAG_BROADCAST )
		{
			ret = send_broadcast( server, data, len );
		}
		else
		{
			ret = send( server->fd, data, len, 0 );
		}

		if ( ret == SOCKET_ERROR )
		{
			return send_error( server, ret );
		}
    }

	register_send( server );

	return INPROGRESS;
}

query_status_t send_error( struct qserver *server, int rc )
{
	unsigned int ipaddr = ntohl(server->ipaddr);
	const char* errstr = strerror(errno);
	fprintf(stderr, "Error on %d.%d.%d.%d: %s, skipping ...\n",
		(ipaddr >> 24) &0xff,
		(ipaddr >> 16) &0xff,
		(ipaddr >> 8) &0xff,
		ipaddr &0xff,
		errstr
	);
	return SYS_ERROR;
}
