#include <sys/time.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <slang.h>

#include "slirc.h"

void DrawLRU(int);
char LRU_dirty = 1;

/* {{{{ LRU        */

#define MAXLRU 50
#define MAXUSTR 256

typedef struct _LRUe {
	struct _LRUe *prev;
	struct _LRUe *next;
	char act __attribute__((packed));
	char str[MAXUSTR] __attribute__((packed));
} LRUe;

typedef struct {
	LRUe *first;
	LRUe *free;
	LRUe x[MAXLRU];
} SLirc_LRU;

static SLirc_LRU *LRU;

static SLirc_LRU *Create_LRU(void)
{
	LRUe *el;
	SLirc_LRU *LRU;

	LRU = (SLirc_LRU *)SLmalloc(sizeof(SLirc_LRU));
	if (LRU) {
		LRU->first = NULL;
		el = LRU->x;
		LRU->free = el;
		for (; el < LRU->x + MAXLRU - 1; el++)
			el->next = el + 1;
		el->next = NULL;
	}
	return LRU;
}

static inline LRUe *GetFree(void)
{
	LRUe *el;
	if ((el = LRU->free))
		LRU->free = el->next;
	return el;
}

static inline void PutFree(LRUe * el)
{
	el->next = LRU->free;
	LRU->free = el;
}

static inline void CutOut(LRUe * el)
{
	el->prev->next = el->next;
	el->next->prev = el->prev;
}

static void PutBefore(LRUe * el, LRUe * el0)
{
	el->prev = el0->prev;
	el->next = el0;
	el0->prev->next = el;
	el0->prev = el;
}

static void MoveBefore(LRUe * el, LRUe * el0)
{
	if (el != el0) {
		CutOut(el);
		PutBefore(el, el0);
	}
}

static int SLirc_user_save(char *st, int *act)
{
	LRUe *el, *el0, *el5 = NULL;
	int len, ord = MAXLRU / 2;
	char *p;

	if (*act == 'm' || *act == 'o')
		ord = 0;

	if ((p = index(st, '!')))
		len = p - st;
	else
		len = strlen(st);

	if (!len)
		return 0;

	el = el0 = LRU->first;
	if (!el) {
		if (*act == 'd')
			return 0;
		el = GetFree();
		el->prev = el;
		el->next = el;
		LRU->first = el;
	} else {
		do {
			if (!ord--)
				el5 = el;
			if (el->str[len] == '!' && !IrcCmp(el->str, st, len)) {	/* found it */

				if (el5) {
					MoveBefore(el, el5);
					if (el5 == el0)
						LRU->first = el;
				}
				el->act = *act;
				return 1;
			}
		} while ((el = el->next) != el0);
		/*  drop-thru is not-found */
		if (*act == 'd')
			return 0;
		if ((el = GetFree())) {
			if (el5) {
				PutBefore(el, el5);
				if (el5 == el0)
					LRU->first = el;
			} else
				PutBefore(el, el0);		/* and leaves it last */

		} else {
			el = el0->prev;
			if (el5) {
				MoveBefore(el, el5);
				if (el5 == el0)
					LRU->first = el;
			}
		}
	}
	strmcpy(el->str, st, MAXUSTR);
	el->act = *act;
	return 0;
}

static char *SLirc_user_find(char *st, int *act)
{
	LRUe *el, *el0, *el5 = NULL;
	int len, ord = 0;

	len = strlen(st);
	if (len > MAXUSTR - 1)
		len = MAXUSTR - 1;

	ord = (*act == 'p' || *act == 'f' || *act == 'd') ? -1 : *act - '0';

	el = el0 = LRU->first;
	if (el) {
		do {
			if (!ord--)
				el5 = el;
			if (!IrcCmp(el->str, st, len)) {	/* found it */

				if (*act != 'p') {
					if (el->str[len] != '!')
						continue;
					if (*act != 'f') {
						if (*act == 'd') {	/* delete it */

							if (el == el->next)
								LRU->first = NULL;
							else {
								CutOut(el);
								if (el == el0)
									LRU->first = el->next;
							}
							PutFree(el);
						} else {						/* advance */

							if (el5) {
								MoveBefore(el, el5);
								if (el5 == el0)
									LRU->first = el;
							}
							el->act = *act;
						}
					}
				}
				return el->str;
			}
		} while ((el = el->next) != el0);
	}
	return NullString;
}

static int SLirc_user_list(void)
{
	LRUe *el, *el0;
	int m = 0, n = 0;

	el = el0 = LRU->first;
	if (el)
		do {
			m++;
			if (n < MAXPMS)
				Rpms[n++] = el->str;
		} while ((el = el->next) != el0);
	Rpms_ct = n;
	return m;
}

static int SLirc_user_nickmod(char *from, char *to)
{
	LRUe *el, *el0;
	char *p, *q;

	el = el0 = LRU->first;
	if (el)
		do {
			if (!IrcCmp(el->str, from, MAXUSTR - 1)) {	/* found it */

				el->act = 'n';
				p = strmcpy(el->str, to, MAXUSTR);	/* p is the terminal null */

				q = index(from, '!');
				if (q)
					strmcpy(p, q, el->str + MAXUSTR - p);
				return 1;
			}
		} while ((el = el->next) != el0);
	return 0;
}


void DrawLRU(int start)
{
	LRUe *el, *el0;
	char *p;
	int Col, len, ix = 0;

	SLsmg_gotorc(start, 0);
	SLsmg_set_color((UseColours) ? ColLRUnorm : 0);

	ix = 0;
	el = el0 = LRU->first;
	if (el) {
		do {
			if (!(p = index(el->str, '!')))
				len = strlen(el->str);
			else
				len = p - el->str;
			if (ix + len > SLtt_Screen_Cols - 2)
				break;
			if (ix) {
				if (UseColours)
					SLsmg_set_color(ColLRUnorm);
				SLsmg_write_char(' ');
				ix++;
			}
			ix += len;
			if (UseColours) {
				Col = ColLRUnorm;
				switch (el->act) {
				case 'd':
					Col = ColLRUgone;
					break;
				case 'j':
					Col = ColLRUjoin;
					break;
				case 'n':
					Col = ColLRUnmod;
					break;
				case 'a':
					Col = ColLRUactn;
					break;
				case 'o':
					Col = ColLRUmesg;
					break;
				case 'm':
				break;
				}
				SLsmg_set_color(Col);
			}
			SLsmg_write_nchars(el->str, len);
		} while ((el = el->next) != el0);
	}
	SLsmg_set_color((UseColours) ? ColLRUnorm : 0);
	SLsmg_erase_eol();
}

/* }}}} LRU        */

static SLang_Intrin_Fun_Type LRU_Fun_Table[] =
{
	MAKE_INTRINSIC_SI("irc_user_save", SLirc_user_save, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SI("irc_user_find", SLirc_user_find, SLANG_STRING_TYPE),
	MAKE_INTRINSIC_0("irc_user_list", SLirc_user_list, SLANG_INT_TYPE),
	MAKE_INTRINSIC_SS("irc_user_nickmod", SLirc_user_nickmod, SLANG_INT_TYPE),
	SLANG_END_TABLE
};

static SLang_IConstant_Type LRU_Constants [] =
{
	MAKE_ICONSTANT("irc_col_lru_norm", ColLRUnorm),
	MAKE_ICONSTANT("irc_col_lru_actn", ColLRUactn),
	MAKE_ICONSTANT("irc_col_lru_mesg", ColLRUmesg),
	MAKE_ICONSTANT("irc_col_lru_gone", ColLRUgone),
	MAKE_ICONSTANT("irc_col_lru_join", ColLRUjoin),
	MAKE_ICONSTANT("irc_col_lru_nmod", ColLRUnmod),
	SLANG_END_TABLE
};

int init_LRU()
{
	LRU = Create_LRU();
	if(SLdefine_for_ifdef("LRU")) return 0;
	return (!SLadd_intrin_fun_table(LRU_Fun_Table, "_LRU") &&
	        !SLadd_iconstant_table (LRU_Constants, NULL));
}


syntax highlighted by Code2HTML, v. 0.9.1