#ifndef KEYBIND_H
#define KEYBIND_H

#include <stdint.h>

#include "app.h"

void keybind_save_buffer(struct app *app, enum keybind_dir dir);

void keybind_mv_cur_char(struct app *app, enum keybind_dir dir);
void keybind_forward_char(struct app *app, enum keybind_dir dir);
void keybind_delete_char(struct app *app, enum keybind_dir dir);

void keybind_mv_cur_word(struct app *app, enum keybind_dir dir);
void keybind_delete_word(struct app *app, enum keybind_dir dir);

void keybind_mv_cur_line(struct app *app, enum keybind_dir dir);
void keybind_mv_cur_edg_line(struct app *app, enum keybind_dir dir);
void keybind_delete_line(struct app *app, enum keybind_dir dir);
void keybind_delete_to_edg_line(struct app *app, enum keybind_dir dir);

void keybind_insert_newline(struct app *app, enum keybind_dir dir);

void keybind_toggle_fps(struct app *app, enum keybind_dir dir);

void keybind_mv_cur_end_buffer(struct app *app, enum keybind_dir dir);
void keybind_insert_last_pressed_key(struct app *app, enum keybind_dir dir);

#if defined(KEYBIND_IMP) || defined(IMP)

void
keybind_save_buffer(struct app *app, enum keybind_dir dir)
{
	(void) dir; (void) app;
	/* buffer_save(app.buf); */
}

void
keybind_mv_cur_char(struct app *app, enum keybind_dir dir)
{
	if ( dir == DIR_FORWARD ) {
		buffer_mv_cur_right(app->cbuf);
	}
	if ( dir == DIR_BACKWARD ) {
		buffer_mv_cur_left(app->cbuf);
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_delete_char(struct app *app, enum keybind_dir dir)
{
	if ( dir == DIR_FORWARD ) {
		buffer_remove_char(app->cbuf, app->cbuf->cur);
	}
	if ( dir == DIR_BACKWARD ) {
		if ( app->cbuf->cur == 0 ) {
			return;
		}
		buffer_remove_char(app->cbuf, --app->cbuf->cur);
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_mv_cur_word(struct app *app, enum keybind_dir dir)
{
	size_t index;
	enum buffer_err err;
	if ( dir == DIR_FORWARD ) {
		index = buffer_index_fw_word(app->cbuf, &err);
		if ( err != BUFFER_ERR_OK ) {
			if ( err == BUFFER_ERR_INVALID_CUR_POS ) {
				app->cbuf->cur = app->cbuf->data.size;
			}
			return;
		}
		app->cbuf->cur = index;
	}
	if ( dir == DIR_BACKWARD ) {
		index = buffer_index_bw_word(app->cbuf, &err);
		if ( err != BUFFER_ERR_OK ) {
			if ( err == BUFFER_ERR_INVALID_CUR_POS ) {
				app->cbuf->cur = app->cbuf->data.size;
			}
			return;
		}
		app->cbuf->cur = index;
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}


void
keybind_delete_word(struct app *app, enum keybind_dir dir)
{
	size_t index, n_rm;
	size_t start, end ;
	enum buffer_err err;
	switch ( dir ) {
	case DIR_BACKWARD: {
		index = buffer_index_bw_word(app->cbuf, &err);
		if ( err != BUFFER_ERR_OK ) {
			if ( err == BUFFER_ERR_INVALID_CUR_POS ) {
				app->cbuf->cur = app->cbuf->data.size;
			}
			return;
		}
		start = index;
		end = app->cbuf->cur;
	} break;
	case DIR_FORWARD: {
		index = buffer_index_fw_word(app->cbuf, &err);
		if ( err != BUFFER_ERR_OK ) {
			if ( err == BUFFER_ERR_INVALID_CUR_POS ) {
				app->cbuf->cur = app->cbuf->data.size;
			}
			return;
		}
		start = app->cbuf->cur;
		end = index;
	} break;
	default: {
		fprintf(stderr, "Got invalid direction in %s: %d",
			__func__, dir);
		exit(EXIT_FAILURE);
	} break;
	}
	n_rm = buffer_remove_between(app->cbuf, start, end, &err);
	if ( err != BUFFER_ERR_OK ) {
		return;
	}
	app->cbuf->cur -= ( dir == DIR_BACKWARD ) * n_rm;
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_mv_cur_line(struct app *app, enum keybind_dir dir)
{
	(void) dir;
	switch ( dir ) {
	case DIR_FORWARD: {
		buffer_mv_cur_down(app->cbuf);
	} break;
	case DIR_BACKWARD: {
		buffer_mv_cur_up(app->cbuf);
	} break;
	default: {
		fprintf(stderr, "Got invalid direction in %s: %d",
			__func__, dir);
		exit(EXIT_FAILURE);
	} break;
	}
}

void
keybind_mv_cur_edg_line(struct app *app, enum keybind_dir dir)
{
	switch ( dir ) {
	case DIR_FORWARD: {
		struct buffer_line l = buffer_find_line(
			app->cbuf, app->cbuf->cur+1);
		app->cbuf->cur = l.end;
	} break;
	case DIR_BACKWARD: {
		struct buffer_line l = buffer_find_line(
			app->cbuf, app->cbuf->cur-1);
		app->cbuf->cur = l.start;
	} break;
	default: {
		fprintf(stderr, "Got invalid direction in %s: %d",
			__func__, dir);
		exit(EXIT_FAILURE);
	} break;
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_delete_line(struct app *app, enum keybind_dir dir)
{
	(void) dir;
	struct buffer_line l = buffer_find_line(app->cbuf, app->cbuf->cur);
	enum buffer_err err;
	l.end += (l.end+1 < app->cbuf->data.size);
	buffer_remove_between(app->cbuf, l.start, l.end, &err);
	if ( err != BUFFER_ERR_OK ) {
		return;
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_delete_to_edg_line(struct app *app, enum keybind_dir dir)
{
	struct buffer_line l = buffer_find_line(app->cbuf, app->cbuf->cur);
	size_t start;
	size_t end;
	switch ( dir ) {
	case DIR_FORWARD: {
		start = app->cbuf->cur;
		end = l.end;
	} break;
	case DIR_BACKWARD: {
		start = l.start;
		end = app->cbuf->cur;
		app->cbuf->cur = l.start;
	} break;
	default: {
		fprintf(stderr, "Got invalid direction in %s: %d",
			__func__, dir);
		exit(EXIT_FAILURE);
	} break;
	}
	enum buffer_err err;
	buffer_remove_between(app->cbuf, start, end, &err);
	if ( err != BUFFER_ERR_OK ) {
		return;
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_insert_newline(struct app *app, enum keybind_dir dir)
{
	(void) dir;
	buffer_insert_char(app->cbuf, '\n', app->cbuf->cur);
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_toggle_fps(struct app *app, enum keybind_dir dir)
{
	(void) dir;
	app->show_fps = ! app->show_fps;
}

void
keybind_mv_cur_end_buffer(struct app *app, enum keybind_dir dir)
{
	switch ( dir ) {
	case DIR_FORWARD: {
		app->cbuf->cur = app->cbuf->data.size;
	} break;
	case DIR_BACKWARD: {
		app->cbuf->cur = 0;
	} break;
	default: {
		fprintf(stderr, "Got invalid direction in %s: %d",
			__func__, dir);
		exit(EXIT_FAILURE);
	} break;
	}
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}

void
keybind_insert_last_pressed_key(struct app *app, enum keybind_dir dir)
{
	(void)dir;
	char c = (char)app->last_pressed_key;
	if ( (c < 32 || c > 126) && c != '\t' ) {
		return;
	}

	buffer_insert_char(app->cbuf, c, app->cbuf->cur);
	app->cbuf->high_col = buffer_calc_cur_col(app->cbuf);
}


#endif /* defined(KEYBIND_IMP) || defined(IMP) */

#endif
