#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gramscii.h"

static int LONG_STEP;

/* line_t and lineset_t management */

void ensure_line_length(line_t *l, int len){
	char *tmp;

	if (l->sz < len + 1){
		tmp = realloc(l->s, (len+1) * 2 * sizeof(char));
		if (!tmp){
			fprintf(stderr, "Unable to allocate string\n");
			exit(1);
		}
		l->s = tmp;
		l->sz = (len + 1) * 2;
	}
}


void alloc_line(line_t *l){
	char *tmp;

	l->sz = WIDTH+1;
	tmp = malloc((l->sz) * sizeof(char));
	if (tmp == NULL){
		fprintf(stderr, "unable to allocate line\n");
		exit(1);
	}
	l->s = tmp;
	memset(l->s, BG, l->sz);
	l->lst = -1;
	l->s[0]='\0';
}

void ensure_num_lines(lineset_t *ls, int n){
	line_t *tmp;

	if (n > ls->sz){
		if (ls->sz == 0)
			ls->l=NULL;
		tmp = realloc(ls->l, (n + LONG_STEP) * sizeof(line_t));
		if (tmp == NULL){
			fprintf(stderr, "Unable to allocate memory for more lines");
			exit(1);
		}
		else {
			ls->l = tmp;
			while ( ls->sz < n + LONG_STEP){
				alloc_line(&(ls->l[ls->sz]));
				ls->sz ++;
			}
		}
	}
}


void dump_lines(lineset_t ls, FILE *f){
	int i;
	for (i=0; i<ls.num ;i++){
		fprintf(f, "%d:%s\n", i, ls.l[i].s);
	}
	fflush(f);
}

void pad_line_to_length(char *s, int W){

	int i;

	for (i=strlen(s); i<W; i++){
		s[i] = BG;
	}
}

/* cut/yank/paste/undo management */

void yank_region(int x1, int y1, int x2, int y2){

	int N, W, i;

	N = y2 - y1 + 1;
	W = x2 - x1 + 1;
	ensure_num_lines(&cutbuf, N);
	
	for (i=y1; i<=y2; i++){
		ensure_line_length(&(cutbuf.l[i-y1]), W);
		memcpy(cutbuf.l[i-y1].s, screen.l[i].s + x1, x2-x1+1);
		if (strlen(cutbuf.l[i-y1].s) < W)
			pad_line_to_length(cutbuf.l[i-y1].s, W);
		cutbuf.l[i-y1].s[W] = '\0';
		cutbuf.l[i-y1].n = i;
	}
	cutbuf.num = N;
#ifdef DEBUG
	dump_lines(cutbuf, stderr);
#endif
	
}


void paste_region(int x1, int y1){
	int i, curlen, pastelen;

	i = y1;
	while( i < HEIGHT && i < y1 + cutbuf.num){
		pastelen = strlen(cutbuf.l[i-y1].s);
		curlen = strlen(screen.l[i].s); 
		memcpy(screen.l[i].s + x1, cutbuf.l[i-y1].s, pastelen);
		if (curlen <= x1)
			/* double-check this line below */
			pad_line_to_length(screen.l[i].s + curlen, x1 - curlen);
		if (curlen <= x1 + pastelen)
			screen.l[i].s[x1 + pastelen] = '\0';
		
		screen.l[i].lst = strlen(screen.l[i].s) - 1;
#ifdef DEBUG 
		fprintf(stderr, "%d.lst: %d\n", i, screen.l[i].lst);
#endif
		i += 1;
		modified = 1;
	}
	redraw();
}

void copy_lines_to_ring(int y1, int y2, int which){
	int i, len, idx;
	lineset_t *tmp;	

	if (y1 > y2){
		y1 ^= y2;
		y2 ^= y1;
		y1 ^= y2;
	}
	if (undo_cur > undo_lst)
		undo_cur = undo_lst;
	if (which == PRV_STATE){ /* adding a new previous state */
		undo_cur += 2;
		idx = undo_cur;
	}
	else
		idx = undo_cur + 1;
	if (idx >= undo_sz - 1){
		undo_sz += 10;
		tmp = realloc(undo, undo_sz * sizeof(lineset_t));
		if (tmp == NULL){
			fprintf(stderr, "Error allocating undo buffer");
			exit(1);
		}
		undo = tmp;
	}
	ensure_num_lines(&(undo[idx]), y2 - y1 + 1);
	for(i=y1; i<=y2; i++){
		len = strlen(screen.l[i].s);
		ensure_line_length(&(undo[idx].l[i-y1]), len);
		strcpy(undo[idx].l[i-y1].s, screen.l[i].s);
		undo[idx].l[i-y1].n = i;
		undo[idx].l[i-y1].lst = screen.l[i].lst;
	}
	undo[idx].num = y2 - y1 + 1;
	if (which == PRV_STATE)
		undo_lst = undo_cur;
#ifdef DEBUG
	fprintf(stderr, "undo_ring: y1: %d y2: %d idx: %d\n", y1, y2, idx);
	for(i=0; i<undo[idx].num; i++){
		fprintf(stderr, "UU: %d| %s\n", undo[idx].l[i].n, undo[idx].l[i].s);
	}
#endif	
}

void invalidate_undo(){
	if (undo_lst > undo_cur)
		undo_lst = undo_cur;
}