#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include <sys/file.h>
#include <netdb.h>
#include <sys/socket.h>

#include "node.h"

static char buf[4096];
static char fmt[256];

/* ---------------------------------------------------------------------- */

static int set_nonblock(int fd, int flag)
{
	long oldflags = fcntl(fd, F_GETFL);
	long newflags;

	if (flag)
		newflags = oldflags | O_NONBLOCK;
	else
		newflags = oldflags & ~((long) O_NONBLOCK);

//	fprintf(stderr, "fd=%d flag=%d 0%lo -> 0%lo\n", fd, flag, oldflags, newflags);

	if (fcntl(fd, F_SETFL, newflags) == -1) {
		log(LOG_ERR, "node_set_nonblock: fnctl(%lo,%lo): %m", fd, newflags);
		logout("fcntl failed");
	}

	return oldflags;
}

static int set_fd_flags(int fd, int flags)
{
	if (fcntl(fd, F_SETFL, flags) == -1) {
		log(LOG_ERR, "node_set_fd_flags: fnctl(%d,&d): %m", fd, flags);
		logout("fcntl failed");
	}

	return 0;
}

/* ---------------------------------------------------------------------- */

void node_set_nonblock(ax25io *io, int flag)
{
	if (io == NULL)
		return;

	if (io->ifd != io->ofd)
		set_nonblock(io->ifd, flag);

	set_nonblock(io->ofd, flag);
}

int node_flush(ax25io *io)
{
	int flags, ret;

	/* make it block */
	flags = set_nonblock(NodeIo->ofd, 0);

	ret = axio_flush(io);

	/* return original state */
	set_fd_flags(NodeIo->ofd, flags);

	return ret;
}

/* ---------------------------------------------------------------------- */

int nputs(const char *str)
{
	return axio_puts(str, NodeIo);
}

int nprintf(const char *format, ...)
{
	va_list args;

	va_start(args, format);
	vsprintf(buf, format, args);
	va_end(args);

	return nputs(buf);
}

int node_msg(const char *format, ...)
{
	va_list args;

	snprintf(fmt, sizeof(fmt), "%s %s\n", NodeId, format);

	va_start(args, format);
	vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	return nputs(buf);
}

int node_msg_block(const char *format, ...)
{
	va_list args;
	int flags, err;

	/* make it block */
	flags = set_nonblock(NodeIo->ofd, 0);

	snprintf(fmt, sizeof(fmt), "%s %s\n", NodeId, format);

	va_start(args, format);
	vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	err = nputs(buf);

	/* return original state */
	set_fd_flags(NodeIo->ofd, flags);

	return err;
}

void node_perror(char *str, int err)
{
	int oldmode, flags;

	buf[0] = 0;
	if (str)
		strcpy(buf, str);
	if (str && err != -1)
		strcat(buf, ": ");
	if (err != -1)
		strcat(buf, strerror(err));

	/* make it block */
	flags = set_nonblock(NodeIo->ofd, 0);

	/* set text mode */
	oldmode = axio_eolmode(NodeIo, EOLMODE_TEXT);

	nputs(NodeId);
	nputs(" ");
	nputs(buf);
	nputs("\n");

	/* return original end-of-line mode */
	axio_eolmode(NodeIo, oldmode);

	axio_flush(NodeIo);

	/* return original blocking state */
	set_fd_flags(NodeIo->ofd, flags);

	log(L_ERROR, buf);
}

char *print_node(const char *alias, const char *call)
{
	static char node[17];

	sprintf(node, "%s%s%s",
		!strcmp(alias, "*") ? "" : alias,
		!strcmp(alias, "*") ? "" : ":",
		call);
	return node;
}

char *print_dl(struct user *u)
{
	static char buf[64];

	switch (u->dl_type) {
	case AF_AX25:
		sprintf(buf, "%s on port %s", u->dl_name, u->dl_port);
		break;
	case AF_NETROM:
	case AF_ROSE:
                sprintf(buf, "%s", u->dl_name);
		break;
	case AF_INET:
		sprintf(buf, "%s:%s", u->dl_name, u->dl_port);
		break;
	}

	return buf;
}

int put_prompt(void)
{
	int err;
	char *p;

	if (strchr(NodePrompt, '%') == NULL)
		err = nputs(NodePrompt);
	else {
		p = expand_string(NodePrompt, 0, NULL);
		err = nputs(p);
		free(p);
	}

	return err;
}

void log(int loglevel, const char *fmt, ...)
{
	static int opened = 0;
	va_list args;
	int pri;

	if (LogLevel < loglevel)
		return;

	if (!opened) {
		openlog("node", LOG_PID, LOG_LOCAL7);
		opened = 1;
	}

	switch (loglevel) {
	case L_ERROR:
		pri = LOG_ERR;
		break;
	case L_LOGIN:
		pri = LOG_NOTICE;
		break;
	case L_GW:
		pri = LOG_INFO;
		break;
	default:
		pri = LOG_INFO;
		break;
	}

	va_start(args, fmt);
	vsnprintf(buf, sizeof(buf), fmt, args);
	buf[sizeof(buf) - 1] = 0;
	va_end(args);

	syslog(pri, "%s", buf);
}

char *strherror(int err)
{
	static char buf[64];

	switch (err) {
	case HOST_NOT_FOUND:
		strcpy(buf, "Unknown host");
		break;
	case TRY_AGAIN:
		strcpy(buf, "Temporary name server error");
		break;
	case NO_RECOVERY:
		strcpy(buf, "Non-recoverable name server error");
		break;
	case NO_ADDRESS:
		strcpy(buf, "No address");
		break;
	default:
		strcpy(buf, "Unknown error");
		break;
	}

	return buf;
}
