/* toansi.c - convert BBS attributes to ANSI colour codes
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mstring.h"

static int mstrtoansi__colours = 1;	 /* flag - 1=colours, 0=no colours */


/* Switch colour processing off (0) or on (1). The default is on. If
 * switched off, no ANSI colour attributes will be output, only bold,
 * reverse, underline etc, so the output will be monochromatic.
 */
void mstrtoansi_colours(int state)
{
	mstrtoansi__colours = state;
}


/* Return a string containing an ANSI escape sequence to change from
 * "current" attributes to "desired". If "desired" is MTR_{FORE,BACK}_COLOUR
 * then the colour is to be changed to "c".
 */
char *mstrtoansi_change(unsigned long *current, int *fg, int *bg,
			unsigned long desired, int c)
{
	static char buf[64];
	unsigned long switch_on;
	int switch_off;
	unsigned long cur;
	int l;

	if ((!current) || (!fg) || (!bg))
		return ("");

	cur = *current;

	if ((cur == desired) && (c < 0))
		return ("");		    /* nothing to change */

	if (desired == MTR_FORE_COLOUR) {   /* change foreground colour */
		if (c == *fg)
			return ("");	    /* no need to change */
		buf[0] = 0;
		if (mstrtoansi__colours) {
			if (c == MTR_COL_DEFAULT) {
				strcpy(buf, "\033[37m\033[39m");
			} else {
				sprintf(buf, "\033[3%dm", c);
			}
		}
		*fg = c;
		return (buf);
	}

	if (desired == MTR_BACK_COLOUR) {   /* change background colour */
		if (c == *bg)
			return ("");	    /* no need to change */
		buf[0] = 0;
		if (mstrtoansi__colours) {
			if (c == MTR_COL_DEFAULT) {
				strcpy(buf, "\033[40m\033[49m");
			} else {
				sprintf(buf, "\033[4%dm", c);
			}
		}
		*bg = c;
		return (buf);
	}

	buf[0] = 0;

	switch_off = 0;

	if ((cur - (desired & cur)) != 0) {
		cur -= (cur & (MTR_BOLD | MTR_UNDERLINE | MTR_STANDOUT
			       | MTR_REVERSE | MTR_FLASH));
		switch_off = 1;
		if (mstrtoansi__colours) {
			if ((*fg == 9) || (*bg == 9)) {
				sprintf(buf,
					"\033[m\033[37m\033[40m\033[3%dm\033[4%dm",
					*fg, *bg);
			} else {
				sprintf(buf, "\033[m\033[3%dm\033[4%dm",
					*fg, *bg);
			}
		} else {
			sprintf(buf, "\033[m");
		}
	}

	switch_on = desired - (desired & cur);
	switch_off = cur - (desired & cur);

	(*current) = desired;

	if (switch_on & MTR_BOLD)
		strcat(buf, "\033[1m");
	if (switch_on & MTR_UNDERLINE)
		strcat(buf, "\033[4m");
	if (switch_on & MTR_STANDOUT)
		strcat(buf, "\033[3m");
	if (switch_on & MTR_REVERSE)
		strcat(buf, "\033[7m");
	if (switch_on & MTR_FLASH)
		strcat(buf, "\033[5m");

	l = strlen(buf);
	if (l < 3)
		return ("");
	buf[l - 1] = 'm';
	return (buf);
}


/* Convert "str" to ANSI colour text from BBS-attributes text, where
 * "str" is a null-terminated buffer whose maximum size is "size".
 *
 * Note that the string may be ended if a newline is found (although a
 * trailing newline is always left if one was there), and tabs are not
 * expanded to spaces.
 *
 * Backspaces are stripped from "str" before processing, with mstripbs().
 */
void mstrtoansi(char *str, long size)
{
	int a, b;
	long c;
	unsigned long current = 0;
	int fg = MTR_COL_DEFAULT;
	int bg = MTR_COL_DEFAULT;
	unsigned long prev;
	unsigned long trib;

	if (!str)
		return;

	mstripbs(str);			    /* remove backspaces */

	a = 0;
	while ((a >= 0) && (str[a] != 0) && (str[a] != 10)) {
		a = mnexttrib(str, a);	    /* find next attribute char */
		if (a < 0)
			break;		    /* no more attributes */

		b = a;
		c = mtribval(str, &a);	    /* find attribute value */
		trib = c - (c & MTR_COLOUR_MASK);	/* strip out any colour value */

		mstrdelete(str, b, a - b);  /* remove attribute from string */
		a = b;

		switch (trib) {		    /* take action on attribute */
		case MTR_BOLD:
		case MTR_UNDERLINE:
		case MTR_STANDOUT:
		case MTR_REVERSE:	    /* switch on */
		case MTR_FLASH:

			mstrinsert(str,
				   mstrtoansi_change(&current, &fg, &bg,
						     current | c, -1), &a,
				   size);
			break;

		case MTR_BOLD_OFF:
		case MTR_UNDERLINE_OFF:
		case MTR_STANDOUT_OFF:
		case MTR_REVERSE_OFF:	    /* switch off */
		case MTR_FLASH_OFF:
		case MTR_ALL_OFF:

			mstrinsert(str,
				   mstrtoansi_change(&current, &fg, &bg,
						     current &
						     (((c & MTR_BOLD_OFF) ?
						       0 : MTR_BOLD) | ((c
									 &
									 MTR_UNDERLINE_OFF)
									? 0
									:
									MTR_UNDERLINE)
						      |
						      ((c &
							MTR_STANDOUT_OFF) ?
						       0 : MTR_STANDOUT) |
						      ((c &
							MTR_REVERSE_OFF) ?
						       0 : MTR_REVERSE) |
						      ((c & MTR_FLASH_OFF)
						       ? 0 : MTR_FLASH)
						     ), -1), &a, size);
			break;

		case MTR_FORE_COLOUR:	    /* change colour */
		case MTR_BACK_COLOUR:

			mstrinsert(str,
				   mstrtoansi_change(&current, &fg, &bg,
						     trib,
						     c & MTR_COLOUR_MASK),
				   &a, size);
			break;

		default:		    /* invalid attribute */

			prev = current;

			mstrinsert(str,
				   mstrtoansi_change(&current, &fg, &bg,
						     MTR_REVERSE |
						     MTR_FLASH, -1), &a,
				   size);
			mstrinsert(str, "*", &a, size);
			mstrinsert(str,
				   mstrtoansi_change(&current, &fg, &bg,
						     prev, -1), &a, size);

			break;
		}
	}
}

/* EOF */
