#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <termios.h>
#include <sys/ioctl.h>

#include "gramscii.h"
#include "config.h"

/*** screen management functions ***/


/*** Status bar ***/

char* mode_str(){
	switch(mode){
		case MOVE:
			return "mov";
		case TEXT:
			return "txt";
		case BOX:
			return "box";
		case ARROW:
			return "arr";
		case DEL:
			return "del";
		case VIS:
			return "vis";
		default:
			return "ERR";
	}
	return "ERR";
}

char get_mark(char dir){
	switch(dir){
		case DIR_U:
			return '^';
		case DIR_D:
			return 'v';
		case DIR_L:
			return '<';
		case DIR_R:
			return '>';
	}
	return '>';
}


void status_bar(){

	if (silent)
		return;
	printf("\033[%d;1f\033[7m", HEIGHT+1);
	printf("%*s", WIDTH-1, "");
	printf("\033[%d;1f\033[7m", HEIGHT+1);
	printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s",
		x, y, mode_str(), line_h, line_v, corner, mark_st, mark_end, "");
	if (!modified)
		printf(" [%s]", fname );
	else
		printf(" *%s*", fname );
#ifdef DEBUG
	printf("  '%d'  ", screen[y].s[x]);
#endif
	printf("\033[0m");
	fflush(stdout);
}

char get_key(FILE *fc, char *msg){

	if (silent)
		return 0;
	printf("\033[%d;1f\033[7m", HEIGHT+1);
	printf("%*s", WIDTH, "");
	printf("\033[%d;1f\033[7m", HEIGHT+1);
	printf("%s", msg);
	fflush(stdout);
	printf("\033[0m");
	fflush(stdout);
	return fgetc(fc);
}

void get_string(FILE *fc, char *msg, char *s, int sz){

	if (!silent){
		printf("\033[%d;1f\033[7m", HEIGHT+1);
		printf("%*s", WIDTH, "");
		printf("\033[%d;1f\033[7m", HEIGHT+1);
	
		/* We must activate echo now */
		t3 = t2;
		t3.c_lflag |= (ECHO | ICANON);
		tcsetattr(0, TCSANOW, &t3);
		printf("%s", msg);
		printf("\033[0m");
	}
	fgets(s, sz, fc);
	s[strlen(s)-1] = '\0';
	tcsetattr(0, TCSANOW, &t2);
	if (!silent)
		fflush(stdout);
}

int is_yes(char c){
	return c=='y' ? 1 : c == 'Y'? 1 : 0;
}

/*** Screen management ***/

void show_cursor(){
	if (silent)
		return;
	printf("\033[%d;%df", y+1, x+1);
	fflush(stdout);
}


void set_xy(int _x, int _y, char c){
	line_t *tmp;
	if (_y >= num_lines){
		tmp = realloc(screen, (_y + LONG_STEP)* sizeof(line_t));
		if (tmp == NULL){
			fprintf(stderr, "Unable to allocate memory for more lines");
			exit(1);
		}
		else while ( num_lines < _y + LONG_STEP){
			screen[num_lines].sz = WIDTH+1;
			screen[num_lines].s = malloc((screen[num_lines].sz) * sizeof(char));
			if (screen[num_lines].s == NULL){
				perror("allocating screen[num_lines].s");
				exit(1);
			}
			memset(screen[num_lines].s, BG, screen[num_lines].sz);
			screen[num_lines].lst = 0;
			screen[num_lines].s[screen[num_lines].lst+1]='\0';
			num_lines ++;
		}
	}
	if (screen[_y].sz < _x + 2){
		screen[_y].sz = (_x +2) * 2;
		screen[_y].s = realloc(screen[_y].s, screen[_y].sz * sizeof(char));
	}
	while (screen[_y].lst<_x){
		screen[_y].lst ++;
		screen[_y].s[screen[_y].lst] = BG;
	}
	screen[_y].s[_x] = c;
	if (_x == screen[_y].lst)
		screen[_y].s[_x+1] = '\0';
}

void set_cur(char c){
	set_xy(x, y, c);
}

void draw_xy(int x, int y, char c){
	/* FIXME: check if x and y are valid!!!! */
	if (silent)
		return;
	printf("\033[%d;%df",y+1,x+1);
	putchar(c);
	fflush(stdout);
}

void update_current(){
	if (silent)
		return;
	printf("\033[%d'%df",y+1,x+1);
	putchar(screen[y].s[x]);
	fflush(stdout);
}

void erase_line(char *s){
	while(*s){
		*s = BG;
		s++;
	}
}

void erase_box(int x1, int y1, char c){
	int x_incr, y_incr, i; 

	x_incr = x1 < x? +1: -1;
	y_incr = y1 < y? +1: -1;
	do{
		i = y1;
		do{
			set_xy(x1, i, c);
		} while(i != y && (1 | (i += y_incr)));
	} while(x1 != x && (1 | (x1 += x_incr)));
	
}

void erase_screen(){
	int i;
	for(i=0;i<HEIGHT; i++)
		erase_line(screen[i].s);
}

void check_bound(){
	if (x<0) x=0;
	else if (x>=WIDTH) x = WIDTH-1;
	if (y<0) y=0;
	else if (y>=HEIGHT) y = HEIGHT -1;
}

void reset_styles(){

	cur_corn = 0;
	corner = corners[0];
	cur_hl = cur_vl = 0;
	cur_start = cur_end = 0;
	line_h = hlines[cur_hl];
	line_v = vlines[cur_vl];
	mark_st = st_marks[cur_start];
	mark_end = end_marks[cur_end];
}

void redraw(){
	int i;

	if (silent)
		return;
	printf("\033[2J\033[1;1H");
	for (i=0;i<HEIGHT;i++){
		fprintf(stdout,"%s\n",screen[i].s);
	}
	status_bar();
	show_cursor();
}

void go_to(int where){
	switch(where){
		case HOME:
			x = y = 0;
			break;
		case END:
			x = WIDTH-1;
			y = HEIGHT-1;
			break;
		case MIDDLE:
			x = WIDTH/2;
			y = HEIGHT/2;
			break;
	}
	check_bound();
	show_cursor();
}

void handle_goto(){
	char c;	
	c=getchar();
	switch(c){
		case 'h':
			dir = DIR_L;
			x = 0;
			break;
		case 'l':
			dir = DIR_R;
			x = WIDTH - 1;
			break;
		case 'j':
			dir = DIR_D;
			y = HEIGHT - 1;
			break;
		case 'k':
			dir = DIR_U;
			y = 0;
			break;
		case 'g':
			dir = DIR_N;
			go_to(HOME);
			break;
		case 'G':
			dir = DIR_N;
			go_to(END);
			break;
		case 'm':
			dir = DIR_N;
			go_to(MIDDLE);
			break;
	}
	check_bound();
	show_cursor();
}


int get_escape(FILE *fc){
	char c[4];
	
	c[0] = fgetc(fc);
	if (c[0] == '['){
		c[1] = fgetc(fc);
		switch(c[1]){
			case 'D':
				dir = DIR_L;
				x -= step;
				break;
			case 'B':
				dir = DIR_D;
				y += step;
				break;
			case 'A':
				dir = DIR_U;
				y -= step;
				break;
			case 'C':
				dir = DIR_R;
				x += step;
				break;
		}
		return 1;
	}
	else{
		ungetc(c[0], fc);
		return 0;
	}

}


int move_around(char c, FILE *fc){

	if (isdigit(c)){
		if (mult)
			mult *=10;
		mult += c - '0';
		return 0;
	}
	switch(c){
		case 27: /* control sequence? */
			c = get_escape(fc);
			break;
		case 'H': step = LONG_STEP;/** FALLTHROUGH **/
		case 'h':
			dir = DIR_L;
			if (mult) 
				step *= mult;
			x -= step;
			break;
		case 'J': step = LONG_STEP;/** FALLTHROUGH **/
		case 'j':
			if (mult) 
				step *= mult;
			dir = DIR_D;
			y += step;
			break;
		case 'K': step = LONG_STEP;/** FALLTHROUGH **/
		case 'k':
			if (mult) 
				step *= mult;
			dir = DIR_U;
			y -= step;
			break;
		case 'L': step = LONG_STEP;/** FALLTHROUGH **/
		case 'l':
			if (mult) 
				step *= mult;
			dir = DIR_R;
			x += step;
			break;
		case 'g':
			handle_goto();
			break;
		default:
			return 0;
	}
	mult = 0;
	return c;
}


void set_video(int v){
	if (silent)
		return;
	printf("\033[%dm", v);
	fflush(stdout);
}


void init_screen(){
	int i;
	struct winsize wsz;
	
	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){
		WIDTH=wsz.ws_col - 2;
		HEIGHT=wsz.ws_row - 1;
	}
	else {
		WIDTH=80;
		HEIGHT=24;
	}
	screen = malloc(HEIGHT * sizeof(line_t));
	num_lines = HEIGHT;
	if (screen == NULL){
		perror("allocating screen");
		exit(1);
	}
	for (i=0; i<HEIGHT; i++){
		screen[i].sz = WIDTH+1;
		screen[i].s = malloc((screen[i].sz) * sizeof(char));
		if (screen[i].s == NULL){
			perror("allocating screen[i].s");
			exit(1);
		}
		memset(screen[i].s, BG, screen[i].sz);
		screen[i].lst = 0;
		screen[i].s[screen[i].lst+1]='\0';
	}
	hlines_sz= sizeof(hlines) -1;
	vlines_sz= sizeof(vlines) -1;
	corners_sz = sizeof(corners) -1;
	stmarks_sz = sizeof(st_marks) - 1;
	endmarks_sz = sizeof(st_marks) - 1;
	reset_styles();
}