/*
 * Copyright (c) 2006 Josef Cejka
 * Copyright (c) 2006 Jakub Vana
 * Copyright (c) 2008 Jiri Svoboda
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup libc
 * @{
 */
/** @file
 */

#include <libc.h>
#include <async.h>
#include <errno.h>
#include <stdio.h>
#include <malloc.h>
#include <vfs/vfs_sess.h>
#include <io/console.h>
#include <ipc/console.h>

console_ctrl_t *console_init(FILE *ifile, FILE *ofile)
{
	console_ctrl_t *ctrl = malloc(sizeof(console_ctrl_t));
	if (!ctrl)
		return NULL;
	
	ctrl->input_sess = fsession(EXCHANGE_SERIALIZE, ifile);
	if (!ctrl->input_sess) {
		free(ctrl);
		return NULL;
	}
	
	ctrl->output_sess = fsession(EXCHANGE_SERIALIZE, ofile);
	if (!ctrl->output_sess) {
		free(ctrl);
		return NULL;
	}
	
	ctrl->input = ifile;
	ctrl->output = ofile;
	ctrl->input_aid = 0;
	
	return ctrl;
}

void console_done(console_ctrl_t *ctrl)
{
	free(ctrl);
}

bool console_kcon(void)
{
	return __SYSCALL0(SYS_DEBUG_ACTIVATE_CONSOLE);
}

void console_flush(console_ctrl_t *ctrl)
{
	fflush(ctrl->output);
}

void console_clear(console_ctrl_t *ctrl)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_0_0(exch, CONSOLE_CLEAR);
	async_exchange_end(exch);
}

int console_get_size(console_ctrl_t *ctrl, sysarg_t *cols, sysarg_t *rows)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	int rc = async_req_0_2(exch, CONSOLE_GET_SIZE, cols, rows);
	async_exchange_end(exch);
	
	return rc;
}

void console_set_style(console_ctrl_t *ctrl, uint8_t style)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_1_0(exch, CONSOLE_SET_STYLE, style);
	async_exchange_end(exch);
}

void console_set_color(console_ctrl_t *ctrl, uint8_t bgcolor, uint8_t fgcolor,
    uint8_t flags)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_3_0(exch, CONSOLE_SET_COLOR, bgcolor, fgcolor, flags);
	async_exchange_end(exch);
}

void console_set_rgb_color(console_ctrl_t *ctrl, uint32_t bgcolor,
    uint32_t fgcolor)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_2_0(exch, CONSOLE_SET_RGB_COLOR, bgcolor, fgcolor);
	async_exchange_end(exch);
}

void console_cursor_visibility(console_ctrl_t *ctrl, bool show)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_1_0(exch, CONSOLE_CURSOR_VISIBILITY, (show != false));
	async_exchange_end(exch);
}

int console_get_color_cap(console_ctrl_t *ctrl, sysarg_t *ccap)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	int rc = async_req_0_1(exch, CONSOLE_GET_COLOR_CAP, ccap);
	async_exchange_end(exch);
	
	return rc;
}

int console_get_pos(console_ctrl_t *ctrl, sysarg_t *col, sysarg_t *row)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	int rc = async_req_0_2(exch, CONSOLE_GET_POS, col, row);
	async_exchange_end(exch);
	
	return rc;
}

void console_set_pos(console_ctrl_t *ctrl, sysarg_t col, sysarg_t row)
{
	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
	async_req_2_0(exch, CONSOLE_GOTO, col, row);
	async_exchange_end(exch);
}

bool console_get_kbd_event(console_ctrl_t *ctrl, kbd_event_t *event)
{
	if (ctrl->input_aid == 0) {
		sysarg_t type;
		sysarg_t key;
		sysarg_t mods;
		sysarg_t c;
		
		async_exch_t *exch = async_exchange_begin(ctrl->input_sess);
		int rc = async_req_0_4(exch, CONSOLE_GET_EVENT, &type, &key, &mods, &c);
		async_exchange_end(exch);
		
		if (rc != EOK) {
			errno = rc;
			return false;
		}
		
		event->type = type;
		event->key = key;
		event->mods = mods;
		event->c = c;
	} else {
		sysarg_t retval;
		async_wait_for(ctrl->input_aid, &retval);
		
		ctrl->input_aid = 0;
		
		if (retval != EOK) {
			errno = (int) retval;
			return false;
		}
		
		event->type = IPC_GET_ARG1(ctrl->input_call);
		event->key = IPC_GET_ARG2(ctrl->input_call);
		event->mods = IPC_GET_ARG3(ctrl->input_call);
		event->c = IPC_GET_ARG4(ctrl->input_call);
	}
	
	return true;
}

bool console_get_kbd_event_timeout(console_ctrl_t *ctrl, kbd_event_t *event,
    suseconds_t *timeout)
{
	struct timeval t0;
	gettimeofday(&t0, NULL);
	
	if (ctrl->input_aid == 0) {
		async_exch_t *exch = async_exchange_begin(ctrl->input_sess);
		ctrl->input_aid = async_send_0(exch, CONSOLE_GET_EVENT,
		    &ctrl->input_call);
		async_exchange_end(exch);
	}
	
	sysarg_t retval;
	int rc = async_wait_timeout(ctrl->input_aid, &retval, *timeout);
	if (rc != EOK) {
		*timeout = 0;
		errno = rc;
		return false;
	}
	
	ctrl->input_aid = 0;
	
	if (retval != EOK) {
		errno = (int) retval;
		return false;
	}
	
	event->type = IPC_GET_ARG1(ctrl->input_call);
	event->key = IPC_GET_ARG2(ctrl->input_call);
	event->mods = IPC_GET_ARG3(ctrl->input_call);
	event->c = IPC_GET_ARG4(ctrl->input_call);
	
	/* Update timeout */
	struct timeval t1;
	gettimeofday(&t1, NULL);
	*timeout -= tv_sub(&t1, &t0);
	
	return true;
}

/** @}
 */
