#include <curses.h>
#include "mucurses.h"

/** @file
 *
 * MuCurses core functions
 *
 */

FILE_LICENCE ( GPL2_OR_LATER );

static void _wupdcurs ( WINDOW *win ) __nonnull;
void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull;
void _wputc ( WINDOW *win, char c, int wrap ) __nonnull;
void _wcursback ( WINDOW *win ) __nonnull;
void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) __nonnull;
void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) __nonnull;
int wmove ( WINDOW *win, int y, int x ) __nonnull;

WINDOW _stdscr = {
	.attrs = A_DEFAULT,
	.ori_y = 0,
	.ori_x = 0,
	.curs_y = 0,
	.curs_x = 0,
	.scr = &_ansi_screen,
};

/*
 *  Primitives
 */

/**
 * Update cursor position
 *
 * @v *win	window in which to update position
 */
static void _wupdcurs ( WINDOW *win ) {
	win->scr->movetoyx ( win->scr, win->ori_y + win->curs_y,
			     win->ori_x + win->curs_x );
}

/**
 * Write a single character rendition to a window
 *
 * @v *win	window in which to write
 * @v ch	character rendition to write
 * @v wrap	wrap "switch"
 */
void _wputch ( WINDOW *win, chtype ch, int wrap ) {
	/* make sure we set the screen cursor to the right position
	   first! */
	_wupdcurs(win);
	win->scr->putc(win->scr, ch);
	if ( ++(win->curs_x) - win->width == 0 ) {
		if ( wrap == WRAP ) {
			win->curs_x = 0;
			/* specification says we should really scroll,
			   but we have no buffer to scroll with, so we
			   can only overwrite back at the beginning of
			   the window */
			if ( ++(win->curs_y) - win->height == 0 )
				win->curs_y = 0;
		} else {
			(win->curs_x)--;
		}
	}
}

/**
 * Write a single character to a window
 *
 * @v *win	window in which to write
 * @v c		character rendition to write
 * @v wrap	wrap "switch"
 */
void _wputc ( WINDOW *win, char c, int wrap ) {
	_wputch ( win, ( ( ( unsigned char ) c ) | win->attrs ), wrap );
}

/**
 * Retreat the cursor back one position (useful for a whole host of
 * ops)
 *
 * @v *win	window in which to retreat
 */
void _wcursback ( WINDOW *win ) {
	if ( win->curs_x == 0 ) {
		if ( win->curs_y == 0 )
			win->curs_y = win->height - 1;
		win->curs_x = win->width = 1;
	} else {
		win->curs_x--;
	}

	_wupdcurs(win);
}

/**
 * Write a chtype string to a window
 *
 * @v *win	window in which to write
 * @v *chstr	chtype string
 * @v wrap	wrap "switch"
 * @v n		write at most n chtypes
 */
void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) {
	for ( ; *chstr && n-- ; chstr++ ) {
		_wputch(win,*chstr,wrap);
	}
}

/**
 * Write a standard c-style string to a window
 *
 * @v *win	window in which to write
 * @v *str	string
 * @v wrap	wrap "switch"
 * @v n		write at most n chars from *str
 */
void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) {
	for ( ; *str && n-- ; str++ ) {
		_wputc ( win, *str, wrap );
	}
}

/**
 * Move a window's cursor to the specified position
 *
 * @v *win	window to be operated on
 * @v y		Y position
 * @v x		X position
 * @ret rc	return status code
 */
int wmove ( WINDOW *win, int y, int x ) {
	/* chech for out-of-bounds errors */
	if ( ( (unsigned)y >= win->height ) ||
	     ( (unsigned)x >= win->width ) ) {
		return ERR;
	}

	win->curs_y = y;
	win->curs_x = x;
	_wupdcurs(win);
	return OK;
}

/**
 * Set cursor visibility
 *
 * @v visibility cursor visibility
 */
int curs_set ( int visibility ) {
	stdscr->scr->cursor ( stdscr->scr, visibility );
	return OK;
}
