/*
 * Copyright (C) 2006 Antti Nykänen <aon@iki.fi>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdorfehs.h"

/* Function prototypes for format char expanders. */
#define RP_FMT(fn) static void fmt_ ## fn (rp_window_elem *elem, struct sbuf *buf)
RP_FMT(framenum);
RP_FMT(lastaccess);
RP_FMT(name);
RP_FMT(number);
RP_FMT(resname);
RP_FMT(resclass);
RP_FMT(status);
RP_FMT(windowid);
RP_FMT(height);
RP_FMT(width);
RP_FMT(incheight);
RP_FMT(incwidth);
RP_FMT(gravity);
RP_FMT(screen);
RP_FMT(xrandrscreen);
RP_FMT(transient);
RP_FMT(maxsize);
RP_FMT(pid);

struct fmt_item {
	/* The format character */
	char fmt_char;

	/* Callback to return the expanded string. */
	void (*fmt_fn) (rp_window_elem *, struct sbuf *);
};

struct fmt_item fmt_items[] = {
	{'a', fmt_resname},
	{'g', fmt_gravity},
	{'h', fmt_height},
	{'H', fmt_incheight},
	{'c', fmt_resclass},
	{'f', fmt_framenum},
	{'i', fmt_windowid},
	{'l', fmt_lastaccess},
	{'M', fmt_maxsize},
	{'n', fmt_number},
	{'p', fmt_pid},
	{'s', fmt_status},
	{'S', fmt_screen},
	{'t', fmt_name},
	{'T', fmt_transient},
	{'w', fmt_width},
	{'W', fmt_incwidth},
	{'x', fmt_xrandrscreen},
	{0, NULL}
};

void
format_string(char *fmt, rp_window_elem *win_elem, struct sbuf *buffer)
{
#define STATE_READ   0
#define STATE_NUMBER 1
#define STATE_ESCAPE 2
	int state = STATE_READ;
	char dbuf[10];
	int width = -1;
	struct sbuf *retbuf;
	int fip, found;

	retbuf = sbuf_new(0);

	for (; *fmt; fmt++) {
		if (*fmt == '%' && state == STATE_READ) {
			state = STATE_ESCAPE;
			continue;
		}
		if ((state == STATE_ESCAPE || state == STATE_NUMBER) &&
		    isdigit(*fmt)) {
			/* Accumulate the width one digit at a time. */
			if (state == STATE_ESCAPE)
				width = 0;
			width *= 10;
			width += *fmt - '0';
			state = STATE_NUMBER;
			continue;
		}
		found = 0;
		if (state == STATE_ESCAPE || state == STATE_NUMBER) {
			if (*fmt == '%')
				sbuf_concat(buffer, "%");
			else {
				for (fip = 0; fmt_items[fip].fmt_char; fip++) {
					if (fmt_items[fip].fmt_char == *fmt) {
						sbuf_clear(retbuf);
						fmt_items[fip].fmt_fn(win_elem,
						    retbuf);
						sbuf_utf8_nconcat(buffer,
						    sbuf_get(retbuf), width);
						found = 1;
						break;
					}
				}
				if (!found) {
					sbuf_printf_concat(buffer, "%%%c", *fmt);
					break;
				}
			}
			state = STATE_READ;
			width = -1;
		} else {
			/* Insert the character. */
			dbuf[0] = *fmt;
			dbuf[1] = 0;
			sbuf_concat(buffer, dbuf);
		}
	}
	sbuf_free(retbuf);
#undef STATE_READ
#undef STATE_ESCAPE
#undef STATE_NUMBER
}

static void
fmt_framenum(rp_window_elem *win_elem, struct sbuf *buf)
{
	if (win_elem->win->frame_number != EMPTY) {
		sbuf_printf_concat(buf, "%d", win_elem->win->frame_number);
	} else
		sbuf_copy(buf, " ");
}

static void
fmt_lastaccess(rp_window_elem *win_elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", win_elem->win->last_access);
}

static void
fmt_name(rp_window_elem *win_elem, struct sbuf *buf)
{
	sbuf_copy(buf, window_name(win_elem->win));
}

static void
fmt_number(rp_window_elem *win_elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", win_elem->number);
}

static void
fmt_resname(rp_window_elem *win_elem, struct sbuf *buf)
{
	if (win_elem->win->res_name)
		sbuf_copy(buf, win_elem->win->res_name);
	else
		sbuf_copy(buf, "None");
}

static void
fmt_resclass(rp_window_elem *win_elem, struct sbuf *buf)
{
	if (win_elem->win->res_class)
		sbuf_copy(buf, win_elem->win->res_class);
	else
		sbuf_copy(buf, "None");
}

static void
fmt_status(rp_window_elem *win_elem, struct sbuf *buf)
{
	rp_window *other_window;

	other_window = find_window_other(rp_current_vscreen);
	if (win_elem->win == other_window)
		sbuf_copy(buf, "+");
	else if (win_elem->win == current_window())
		sbuf_copy(buf, "*");
	else
		sbuf_copy(buf, "-");
}

static void
fmt_windowid(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%ld", (unsigned long) elem->win->w);
}

static void
fmt_height(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", elem->win->height);
}

static void
fmt_width(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", elem->win->width);
}

static void
fmt_incheight(rp_window_elem *elem, struct sbuf *buf)
{
	int height;
	height = elem->win->height;

	if (elem->win->hints->flags & PResizeInc)
		height /= elem->win->hints->height_inc;

	sbuf_printf_concat(buf, "%d", height);
}

static void
fmt_incwidth(rp_window_elem *elem, struct sbuf *buf)
{
	int width;
	width = elem->win->width;

	if (elem->win->hints->flags & PResizeInc)
		width /= elem->win->hints->width_inc;

	sbuf_printf_concat(buf, "%d", width);
}

static void
fmt_gravity(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_copy(buf, wingravity_to_string(elem->win->gravity));
}

static void
fmt_screen(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", elem->win->vscreen->screen->screen_num);
}

static void
fmt_xrandrscreen(rp_window_elem *elem, struct sbuf *buf)
{
	sbuf_printf_concat(buf, "%d", elem->win->vscreen->screen->xrandr.output);
}

static void
fmt_transient(rp_window_elem *elem, struct sbuf *buf)
{
	if (elem->win->transient)
		sbuf_concat(buf, "Transient");
}

static void
fmt_maxsize(rp_window_elem *elem, struct sbuf *buf)
{
	if (elem->win->hints->flags & PMaxSize)
		sbuf_concat(buf, "Maxsize");
}

static void
fmt_pid(rp_window_elem *elem, struct sbuf *buf)
{
	struct rp_child_info *info;

	info = get_child_info(elem->win->w, 0);
	if (info)
		sbuf_printf_concat(buf, "%d", info->pid);
	else
		sbuf_concat(buf, "?");
}
