/*	Copyright (C) 1990, 1992, 1993 Free Software Foundation, Inc.

This file is part of Oleo, the GNU Spreadsheet.

Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <ctype.h>
#include "funcdef.h"
#define obstack_chunk_alloc ck_malloc
#define obstack_chunk_free free
#include "obstack.h"
#include "sysdef.h"

#include "global.h"
#include "cell.h"
#include "eval.h"
#include "errors.h"

struct value {
	int	type;
	union vals x;
};

#define Float	x.c_d
#define String	x.c_s
#define Int	x.c_l
#define Value	x.c_i
#define Rng	x.c_r

#define ERROR(x)	\
 {			\
	p->Value=x;	\
	p->type=TYP_ERR;\
	return;		\
 }
	

extern struct obstack tmp_mem;

extern char *flt_to_str();

static void
do_edit (numarg,p)
     int numarg;
      struct value * p;
{
	int mm;
	int add_len;
	int tmp_len;
	char *ptr1,*ptr2,*retp;
	int off1,off2;

	if(numarg<3)
		ERROR(BAD_INPUT);
	for(mm=3,add_len=0;mm<numarg;mm++)
		add_len+=strlen((p+mm)->String);
	tmp_len=strlen(p->String);
	off1=(p+1)->Int;
	off2=(p+2)->Int;
	if(off1==0 || tmp_len < ((off1<0) ? -off1 : off1) ||
	   off2==0 || tmp_len < ((off2<0) ? -off2 : off2))
		ERROR(OUT_OF_RANGE);
	ptr1=p->String + (off1>0 ? off1-1 : tmp_len+off1);
	ptr2=p->String + 1 + (off2>0 ? off2-1 : tmp_len+off2);
	if(ptr1>ptr2)
		ERROR(OUT_OF_RANGE);
	retp=obstack_alloc(&tmp_mem,add_len+tmp_len-(ptr2-ptr1));
	strncpy(retp,p->String,ptr1-p->String);
	retp[ptr1-p->String]='\0';
	for(mm=3;mm<numarg;mm++)
		strcat(retp,(p+mm)->String);
	strcat(retp,ptr2);
	p->String=retp;
	p->type=TYP_STR;
}

static void
do_repeat (p)
     struct value * p;
{
	char *str = (p  )->String;
	long num  = (p+1)->Int;

	char *ret;
	char *strptr;
	int len;

	if(num<0)
		ERROR(OUT_OF_RANGE);
	len=strlen(str);
	ret=strptr=obstack_alloc(&tmp_mem,len*num+1);
	while(num--) {
		if (len)
		  bcopy(str,strptr,len);
		strptr+=len;
	}
	*strptr=0;
	p->String=ret;
}

static void
do_len (p)
     struct value * p;
{
	long ret;
	char *ptr;

	for(ret=0,ptr=p->String;*ptr;ret++,ptr++)
		;
	p->Int=ret;
	p->type=TYP_INT;
}

static void
do_up_str (p)
     struct value * p;
{
	char *s1,*s2;
	char *strptr;

	strptr=obstack_alloc(&tmp_mem,strlen(p->String)+1);
	for(s1=strptr,s2=p->String;*s2;s2++)
		*s1++ = (islower(*s2) ? toupper(*s2) : *s2);
	*s1=0;
	p->String=strptr;
}

static void
do_dn_str (p)
     struct value * p;
{
	char *s1,*s2;
	char *strptr;

	strptr=obstack_alloc(&tmp_mem,strlen(p->String)+1);
	for(s1=strptr,s2=p->String;*s2;s2++)
		*s1++ = (isupper(*s2) ? tolower(*s2) : *s2);
	*s1=0;
	p->String=strptr;
}

static void
do_cp_str (p)
     struct value * p;
{
	char *strptr;
	char *s1,*s2;
	int wstart=1;

	strptr=obstack_alloc(&tmp_mem,strlen(p->String)+1);
	for(s1=strptr,s2=p->String;*s2;s2++) {
		if(!isalpha(*s2)) {
			wstart=1;
			*s1++= *s2;
		} else if(wstart) {
			*s1++ = (islower(*s2) ? toupper(*s2) : *s2);
			wstart=0;
		} else
			*s1++ = (isupper(*s2) ? tolower(*s2) : *s2);
	}
	*s1=0;
	p->String=strptr;
}

static void
do_trim_str (p)
     struct value * p;
{
	char *s1,*s2;
	int sstart=0;
	char *strptr;

	strptr=obstack_alloc(&tmp_mem,strlen(p->String)+1);
	for(s1=strptr,s2=p->String;*s2;s2++) {
		if(!isascii(*s2) || !isprint(*s2))
			continue;
		if(*s2==' ') {
				if(sstart) {
				*s1++= *s2;
				sstart=0;
			}
		} else {
			sstart=1;
			*s1++= *s2;
		}
	}
	*s1=0;
	p->String=strptr;
}

static void
do_concat ( numarg,p)
     int  numarg;
      struct value * p;
{
	int cur_string;
	char *s;
	char buf[40];
	CELLREF crow,ccol;
	CELL *cell_ptr;

	for(cur_string=0;cur_string<numarg;cur_string++) {
		switch(p[cur_string].type) {
		case 0:
			continue;
		case TYP_RNG:
			for(crow=p[cur_string].Rng.lr;crow<=p[cur_string].Rng.hr;crow++)
				for(ccol=p[cur_string].Rng.lc;ccol<=p[cur_string].Rng.hc;ccol++) {
					if(!(cell_ptr=find_cell(crow,ccol)))
						continue;
					switch(GET_TYP(cell_ptr)) {
					case 0:
						break;
					case TYP_STR:
						(void)obstack_grow(&tmp_mem,cell_ptr->cell_str,strlen(cell_ptr->cell_str));
						break;
					case TYP_INT:
						sprintf(buf,"%ld",cell_ptr->cell_int);
						(void)obstack_grow(&tmp_mem,buf,strlen(buf));
						break;
					case TYP_FLT:
						s=flt_to_str(cell_ptr->cell_flt);
						(void)obstack_grow(&tmp_mem,s,strlen(s));
						break;
					default:
						(void)obstack_finish(&tmp_mem);
						ERROR(NON_STRING);
					}
			}
			break;
		case TYP_STR:
			s=p[cur_string].String;
			(void)obstack_grow(&tmp_mem,s,strlen(s));
			break;
		case TYP_INT:
			sprintf(buf,"%ld",p[cur_string].Int);
			(void)obstack_grow(&tmp_mem,buf,strlen(buf));
			break;
		case TYP_FLT:
			s=flt_to_str(p[cur_string].Float);
			(void)obstack_grow(&tmp_mem,s,strlen(s));
			break;
		default:
			(void)obstack_finish(&tmp_mem);
			ERROR(NON_STRING);
		}
	}
	(void)obstack_1grow(&tmp_mem,0);
	p->type=TYP_STR;
	p->String=(char *)obstack_finish(&tmp_mem);
}


static void
do_mid (p)
     struct value * p;
{
	char *str = (p  )->String;
	long from = (p+1)->Int-1;
	long len =  (p+2)->Int;

	char	*ptr1;
	int tmp;

	tmp=strlen(str);

	if(from<0 || len<0)
		ERROR(OUT_OF_RANGE);
	ptr1=(char *)obstack_alloc(&tmp_mem,len+1);
	if(from>=tmp || len==0)
		ptr1[0]='\0';
	else {
		strncpy(ptr1,str+from,len);
		ptr1[len]='\0';
	}
	p->String=ptr1;
}


static void
do_substr (p)
     struct value * p;
{
	long off1 = (p  )->Int;
	long off2 = (p+1)->Int;
	char *str = (p+2)->String;

	char	*ptr1,	*ptr2;
	int tmp;
	char *ret;

	tmp=strlen(str);
	if(off1==0 || tmp < ((off1<0) ? -off1 : off1) ||
	   off2==0 || tmp < ((off2<0) ? -off2 : off2))
		ERROR(OUT_OF_RANGE);
	ptr1=str + (off1>0 ? off1-1 : tmp+(off1));
	ptr2=str + (off2>0 ? off2-1 : tmp+(off2));

	if(ptr1>ptr2)
		ERROR(OUT_OF_RANGE);
	tmp=(ptr2-ptr1)+1;
	ret=(char *)obstack_alloc(&tmp_mem,tmp+1);
	strncpy(ret,ptr1,tmp);
	ret[tmp]=0;
	p->String=ret;
	p->type=TYP_STR;
}

static void
do_strstr (p)
     struct value * p;
{
	char *strptr	= (p  )->String;
	char *str1	= (p+1)->String;
	long off	= (p+2)->Int;
	char *ret;

	if(off<1 || strlen(strptr)<=off-1)
		ERROR(OUT_OF_RANGE);
	ret=(char *)strstr(strptr+off-1,str1);
	p->Value= ret ? 1 + ret-strptr : 0;
	p->type=TYP_INT;
}

struct function string_funs[] = {
{ C_FN1,	X_A1,	"S",    do_len,		"len" },
{ C_FN3,	X_A3,	"SSI",  do_strstr,	"find" },

{ C_FN1,	X_A1,	"S",    do_up_str,	"strupr" },
{ C_FN1,	X_A1,	"S",    do_dn_str,	"strlwr" },
{ C_FN1,	X_A1,	"S",    do_cp_str,	"strcap" },
{ C_FN1,	X_A1,	"S",    do_trim_str,	"trim" },

{ C_FN3,	X_A3,	"IIS",  do_substr,	"substr" },
{ C_FN3,	X_A3,	"SII",  do_mid,		"mid" },

{ C_FN2,	X_A2,	"SI",   do_repeat,	"repeat" },
{ C_FNN,	X_AN,	"EEEE", do_concat,	"concat" },
{ C_FNN,	X_AN,	"SIIS", do_edit,	"edit" },
{ 0,		0,	{0},	0,		0 }
};

