/****************************************************************************
 *
 *	earray : euler's array of command, output and comment strings
 *
 ****************************************************************************/

#include "earray.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>

#define	ALLOC_CHUNK	16
#define ALLOC_SIZE(n) (ALLOC_CHUNK*(((n)/ALLOC_CHUNK)+1))


typedef struct estring {
	GString *	str;
	int			type;
} estring;

struct earray {
	estring *	data;
	int			len;
	int			alloc;
};


static int watchforrealloc(earray *a, int n);

/*---------------------------------------------------------------------------
 *	constructor / destructor
 *---------------------------------------------------------------------------*/

earray * e_new()
{
	earray *a;
	
	a = (earray*)malloc(sizeof(earray));
	if (a) {
		a->data = (estring*)malloc(ALLOC_CHUNK*sizeof(estring));
		if (a->data) {
			a->alloc = ALLOC_CHUNK;
			a->len = 0;
			return a;
		}
		free(a);
	}
	
	return NULL;
}

void e_free(earray *a)
{
	int i;
	
	if (!a) return;
	
	for (i=0 ; i<a->len ; i++) {
		g_string_free(a->data[i].str,TRUE);
	}
	
	free(a->data);
	free(a);
}

int e_load(earray *a, char *filename)
{
	FILE *in;
	DIR *dir;
	char line[1024];
	int type;

	if (!a) return 0;
	
	/*	try to see if it is not a directory (because fopen succeeds even if
		it is a directory... */
	dir = opendir(filename);
	if (dir) {
		fprintf(stderr,"It must be a file. I can't open %s !\n",filename);
		closedir(dir);
		return 0;
	
	}
	
	/*	load the notebook */
	in=fopen(filename,"r");
	if (!in)
	{
		fprintf(stderr,"Could not open the file %s !\n",filename);
		return 0;
	}
	
	/*	clear the old notebook */
	e_clear(a);
	
	while (!feof(in)) {
		int n;
		if (!fgets(line,1024,in)) break;
		n=strlen(line);
		if (n>=2 && line[n-2]=='\r') line[n-2]=0; 
		if (line[n-1]=='\n') line[n-1]=0;
		switch (line[0]) {
			case '>':type=E_PROMPT;break;
			case '$':type=E_UDF;break;
			case '%':type=E_COMMENT;break;
			default:type=E_OUTPUT;
		}
		e_append(a,line,type);
	}
	fclose(in);
	
	/*	if the notebook file was empty... */
	if (!a->len) e_append(a,"",E_OUTPUT);
	
	return 1;
}

int e_save(earray *a, char *filename)
{
	FILE *out;
	int i;
	
	if (!a) return 0;
	
	out=fopen(filename,"w");
	if (!out) {
		fprintf(stderr,"Could not save to the file %s !\n",filename);
		return 0;
	}

	for (i=0 ; i<a->len ; i++) {
		char ch = a->data[i].str->str[0];
		if (a->data[i].type==E_OUTPUT && (ch=='>' || ch=='$' || ch=='%'))
			fputc(' ',out);
		fputs(a->data[i].str->str,out);
		fputc('\n',out);
	}

	fclose(out);
	
	return 1;
}

int e_get_length(earray *a)
{
	if (a) return a->len;
	return 0;
}

/*---------------------------------------------------------------------------
 *	add / remove lines to the array
 *---------------------------------------------------------------------------*/

void e_clear(earray *a)
{
	int i;
	
	if (!a) return;
	
	for (i=0 ; i<a->len ; i++) {
		g_string_free(a->data[i].str,TRUE);
	}
	
	a->len = 0;
	
	watchforrealloc(a,0);
}

int e_append(earray *a, char *text, int type)
{
	if (a) {
		GString *s = g_string_new(text);
		
		if (s) {
			if (watchforrealloc(a,a->len+1)) {
				a->data[a->len].str = s;
				a->data[a->len].type = type;
				a->len++;
				return 1;
			}
			g_string_free(s,TRUE);
		}
	}
	return 0;
}

int e_insert(earray *a, int index, char *text, int type)
{
	if (a) {
		GString *s;
		
		if (index>=a->len) return e_append(a,text,type);
		if (index<0) index=0;
		
		s = g_string_new(text);
		
		if (s) {
			if (watchforrealloc(a,a->len+1)) {
				memmove(&(a->data[index+1]),&(a->data[index]),(a->len-index)*sizeof(estring));
				a->data[index].str = s;
				a->data[index].type = type;
				a->len++;
				return 1;
			}
			g_string_free(s,TRUE);
		}
	}
	return 0;
}

void e_remove(earray *a, int index)
{
	if (!a) return;
	if (index<0 || index>=a->len-1) return;
	
	memmove(&(a->data[index]),&(a->data[index+1]),(a->len-index)*sizeof(estring));
	
	a->len--;
	watchforrealloc(a,a->len); 
}

/*---------------------------------------------------------------------------
 *	set / get things in specified line in the array
 *---------------------------------------------------------------------------*/

void e_set_type(earray *a, int index, int type)
{
	if (!a) return;
	if (index<0 || index>=a->len) return;
	
	a->data[index].type = type;
}

int e_get_type(earray *a, int index)
{
	if (a && index>=0 && index<a->len)
		return a->data[index].type;

	return 0;
}

void e_set_text(earray *a, int index, char *text)
{
	if (!a) return;
	if (index<0 || index>=a->len) return;
	
	g_string_assign(a->data[index].str,text);
}

char * e_get_text(earray *a, int index)
{
	if (a && index>=0 && index<a->len)
		return a->data[index].str->str;

	return NULL;
}

void e_append_char(earray *a, int index, char c)
{
	if (a && index>=0 && index<a->len)
		g_string_append_c(a->data[index].str,c);
}

void e_append_text(earray *a, int index, char *text)
{
	if (a && index>=0 && index<a->len)
		g_string_append(a->data[index].str,text);
}

void e_insert_char(earray *a, int index, int pos, char c)
{
	if (a && index>=0 && index<a->len)
		g_string_insert_c(a->data[index].str,pos,c);
}

void e_insert_text(earray *a, int index, int pos, char *text)
{
	if (a && index>=0 && index<a->len)
		g_string_insert(a->data[index].str,pos,text);
}

void e_remove_text(earray *a, int index, int pos, int len)
{
	if (a && index>=0 && index<a->len)
		g_string_erase(a->data[index].str,pos,len);
}

int e_get_text_length(earray *a, int index)
{
	return a->data[index].str->len;
}

/*---------------------------------------------------------------------------
 *	watch if the array should be resized
 *---------------------------------------------------------------------------*/

static int watchforrealloc(earray *a, int n)
{
	int alloc = ALLOC_SIZE(n);
	if (alloc!=a->alloc) {
		estring *data;
		data = (estring*)realloc(a->data,alloc*sizeof(estring));
		if (data) {
			a->data = data;
			a->alloc = alloc;
			return 1;
		}
		return 0;
	}
	return 1; 
}
