#include "include/glbtypes.h"
#include "include/glbproto.h"
#include "include/globvars.h"

/**************************** Sorts & Inserts Ops *****************************/

void opdef_ins(defined_op op, defined_op oda)
{
cmp_str res1;
defined_op ind;
int step1,formind;
char tok[33],mtch[33];

if (op->attr==255)  /* junk buffer */
	{
	ind=opdef_last(oda);
	ind->next=op;
	op->prev=ind;
	op->next=NULL;
	return;
	}

for(formind=0,ind=oda;ind!=NULL;)
	{
	while((op->form[formind]=='x')||(op->form[formind]=='l')||(op->form[formind]=='t'))
		{
		while(ind!=NULL)
			{
			if(ind->form==NULL)
				break;
			else
				if(ind->form[formind]!='f')
					break;

			if((ind->next!=NULL)&(ind->attr!=255))
				ind=ind->next;
			else
				{
				formind++;
				continue;
				};
			};
		if (op->form[formind]=='l')
			while(ind!=NULL)
				{
				if(ind->form==NULL)
					break;
				else
					if((ind->form[formind]!='x')&&(ind->form[formind]!='t'))
						break;

				if(ind->next!=NULL)
					ind=ind->next;
				else
					{
					formind++;
					continue;
					};
				};	
		formind++;
		};

	for(;(ind!=NULL);ind=ind->next)
		{
		if(ind->attr==255) /* finish ! */
			{
			ind=ind->prev;
			op->next=ind->next;
			op->next->prev=op;
			op->prev=ind;
			ind->next=op;
			return;
			};

		if(ind->form[formind]=='f')
			{
			Strncpy(tok,get_nth_token(formind+1,ind->defn),32);
			for(step1=0;(!!tok[step1])&(tok[step1]!=' ');step1++){};tok[step1]=0;
			Strncpy(mtch,get_nth_token(formind+1,op->defn),32);
			for(step1=0;(!!mtch[step1])&(mtch[step1]!=' ');step1++){};mtch[step1]=0;
			res1=str_cmp(tok,mtch);
			if ((res1!=right)&(res1!=rlong))
				{
				if(ind->prev!=NULL)
					ind=ind->prev;
				break;
				};
			}
		else		/* ind->form[formind] is 'x' or 'l' or 't' */
			/* op->form[formind] is at least supposed to be 'f' */ 
			{
			if (ind->prev!=NULL) 
				ind=ind->prev;
			op->next=ind->next;
			if(ind->next!=NULL) 
				ind->next->prev=op;
			ind->next=op;
			op->prev=ind;
			return;
			};

		if (ind->next==NULL) 
			break;
		};

	if (res1==eq) 
		formind++; 
	else 
		break;
	};

op->next=ind->next;
if(ind->next!=NULL) 
	ind->next->prev=op;
ind->next=op;
op->prev=ind;
}

/* end opdef_ins */

/*************************** Gets last Op Def ***************************************/
defined_op opdef_last(defined_op l)
{
while((l!=NULL)&(l->next!=NULL)) l=l->next;
return l;
}
/*************************** End opdef_last ***************************************/

void print_opd(defined_op l)
{
if (l!=NULL)
	{
	while((l!=NULL))
		if(l->attr!=255)
			{
			printf("   [%s] %*c %19s",l->form,20-strlen(l->form),'³',l->defn);
			if (l->fdef.opdef!=NULL)
				{
				printf("        Have fdef\n");
				}
			else
				printf("        No fdef\n");
			l=l->next;
			}
		else
			break;
	};
}



/******************************* Counts Op Defs ************************************/

int opdef_count(defined_op d_op)
{
	int howmuch;
	for(howmuch=0;d_op!=NULL;d_op=d_op->next,howmuch++) {};
	return howmuch;
}




inline_op copy_inline_op_list(inline_op src)
{
inline_op res,ind;
res=ind=my_malloc(sizeof(struct op_definition));
*res=*src;
src=src->next;
res->prev=NULL;
while(src!=NULL)
	{
	ind->next=my_malloc(sizeof(struct op_definition));
	*(ind->next)=*src;
	ind->next->prev=ind;
	src=src->next;
	ind=ind->next;
	};
ind->next=NULL;
return res;
}

inline_op copy_inline_op_list_part(inline_op from,inline_op to)
{
inline_op res,ind;

if(from==NULL)
	{
	fprintf(stderr,"Error:copy_inline_op_list_part(): from==NULL\n");
	abort();
	};

res=ind=my_malloc(sizeof(struct op_definition));
*res=*from;
from=from->next;
res->prev=NULL;
while((from!=to)&(from!=NULL))
	{
	ind->next=my_malloc(sizeof(struct op_definition));
	*(ind->next)=*from;
	ind->next->prev=ind;
	ind=ind->next;
	from=from->next;
	};

if((from==NULL)&(to!=NULL))
	{
	fprintf(stderr,"Error:copy_inline_op_list_part(): (from==NULL)&(to!=NULL)\n");
	abort();
	};

ind->next=NULL;
return res;
}


void destroy_inline_op_list(inline_op src)
{
inline_op tmp;
while(src!=NULL)
	{
	tmp=src->next;
	Free(src);
	src=tmp;
	};
}

void destroy_inline_op_list_part(inline_op from,inline_op to)
{
inline_op tmp;

if(from==NULL)
	{
	fprintf(stderr,"Error:destroy_inline_op_list_part(): from==NULL\n");
	abort();
	};

while((from!=NULL)&(from!=to))
	{
	tmp=from->next;
	Free(from);
	from=tmp;
	};

if((from==NULL)&(to!=NULL))
	{
	fprintf(stderr,"Error:destroy_inline_op_list_part(): (from==NULL)&(to!=NULL)\n");
	abort();
	};

}


int inline_op_list_count(inline_op i_op)
{
	int howmuch;
	for(howmuch=0;i_op!=NULL;i_op=i_op->next,howmuch++) {};
	return howmuch;
}

int inline_op_list_count_part(inline_op from,inline_op to)
{
int howmuch=0;

if(from==NULL)
	return 0;

while((from!=to)&(from!=NULL))
	{
	from=from->next;
	howmuch++;
	};

if((from==NULL)&(to!=NULL))
	{
	fprintf(stderr,"Error:inline_op_list_count_part(): (from==NULL)&(to!=NULL)\n");
	abort();
	};

return howmuch;
}

/*************************** End opdef_count **********************************************/


/*************************** Gets last Inline Op Def ***************************************/
inline_op inline_opdef_last(inline_op l)
{
while((l!=NULL)&(l->next!=NULL)) l=l->next;
return l;
}
/*************************** End inline_opdef_last ***************************************/



listnode inline_op_find_var(char PTR name,listnode lst)
{
listnode l1=NULL;
if (lst==NULL) return NULL;
while((lst!=NULL))
	{
	if((lst->attr==VARIABLE)&(!Strcmp(name,lst->data)))
		return lst;
	if ((lst->attr==TWOLISTS)||(lst->attr==FUNCAVAR))
		l1=inline_op_find_var(name,(listnode) lst->data);
	if(l1!=NULL) return l1;
	lst=lst->next;
	};
return lst;
}

listnode inline_op_find_prefix(char PTR pref,listnode lst)
{
listnode l1=NULL;
if (lst==NULL) return NULL;
while((lst!=NULL))
	{
	if((lst->attr==VARIABLE)&(!Strncmp(pref,lst->data,Strlen(pref))))
		return lst;
	if ((lst->attr==TWOLISTS)||(lst->attr==FUNCAVAR))
		l1=inline_op_find_prefix(pref,(listnode) lst->data);
	if(l1!=NULL) return l1;
	lst=lst->next;
	};
return lst;
}



listnode inline_op_subst_lvar(char PTR name,listnode lst, listnode l)
{
listnode l1=NULL;
if (lst==NULL) return NULL;
while((lst!=NULL))
	{
	if((lst->attr==VARIABLE)&(!Strcmp(name,lst->data)))
		{
		Free(lst->data);
		lst->attr=l->attr;
		lst->data=l->data;
		lst->next=l->next;
		return lst;
		};
	if ((lst->attr==TWOLISTS)||(lst->attr==FUNCAVAR))
		l1=inline_op_subst_lvar(name,(listnode) lst->data, l);
	if(l1!=NULL) return l1;
	lst=lst->next;
	};
return lst;
}

int get_inl_varinfo(char PTR name, struct inline_varinfo PTR lst)
{
while ((!!Strcmp(name,lst->name))&(lst!=NULL))
	lst=lst->next;
if (lst==NULL) return -1;
return lst->times_used;
}


void mk_opd(struct op_definition PTR res,char * form,char * defn,int attr,int prec)
{
	res->prec=prec;
	res->attr=attr;
	res->form=form;
	res->defn=defn;
	res->vars=NULL; /* TEMPORARY !!! */
	return ;
}

void install_opd(defined_op base,char * form,char * defn,int attr,int prec)
{
defined_op d_op;
d_op=my_malloc(sizeof(struct op_definition));
mk_opd(d_op,form,defn,attr,prec);
d_op->fdef.opdef=NULL;
opdef_ins(d_op,base);
}


void finstall_opd(defined_op base, char * form, char * defn, char * fdef,int attr,int prec)
{
defined_op d_op;
d_op=my_malloc(sizeof(struct op_definition));
if(d_op==NULL)
	{
	printf("Out of memory at finstall_opd(base,form,defn,fdef,attr,prec)\n");
	printf("form=\"%s\",defn=\"%s\",fdef=\"%s\",attr=%d,prec=%d\n",form,defn,fdef,attr,prec);
	printf("Can't allocate memory for structure 'd_op' (size %d bytes)\n",sizeof(struct op_definition));
	exit(1);
	};

mk_opd(d_op,form,defn,attr,prec);
d_op->fdef.opdef=mk_set_lnd(NULL,NULL,VARIABLE);

if(fdef!=NULL)
	{
	if (!get_function(fdef,d_op->fdef.opdef,base,NULL,0,NULL,NULL,NULL))
		{
		fprintf(stderr,"When installing operator %s\nwhile parsing function %s\nerror occured !",defn,fdef);
		exit(1);
		}
	else
		{
		d_op->fdef.opdef=rm_cdr_next(d_op->fdef.opdef);
	/*	printlist2(d_op->fdef,0,1); */
		};
	};
opdef_ins(d_op,base);
}


/*int install_iopd(inline_op base,char * form,char * defn,int attr,int prec)
{
inline_op d_op;
d_op=my_malloc(sizeof(struct inline_op_defn));
*d_op=(inline_op)mk_opd(form,defn,attr,prec);
d_op->fdef=NULL;
opdef_ins(d_op,base);
};*/

int get_opdef_def(
char * instr,
defined_op op,
listnode iop,
struct vardecl_listoflist * vars,
struct type_listoflist * types,
listnode fnlist)
	{
	struct vardecl_listoflist tmpvarll;
	int mode=0; /* 1 - usual operator, 2 - inline operator, keyword */
	char * str,* defn=NULL,* tmp=NULL;
	int prec,i,j;
	struct vardecl_list * opvars=NULL;
	defined_op res;
	char form[34];
	listnode ltmp=NULL;

	tmpvarll.next=vars;
	tmpvarll.vlist=NULL;

	if(!Strncmp(instr,"#op ",4))
		{
		str=instr+4;
		mode=1;
		}
	else if(!Strncmp(instr,"#iop ",5))
		{
		str=instr+5;
		mode=2;
		}
	else
		return 0;

	while(*str==' ')
		str++;
	refresh_buffer(instr,str-instr,64);
	str=get_variable(instr);
	memset(form,0,sizeof(form));
	strncpy(form,instr,str-instr);
	prec=atoi(form);
	while(*str==' ')
		str++;
	refresh_buffer(instr,str-instr,64);
	str=get_variable(instr);
	memset(form,0,sizeof(form));
	strncpy(form,instr,str-instr);
	while(*str==' ')
		str++;
	refresh_buffer(instr,str-instr,64);

	defn=my_malloc(strlen(form)*32+strlen(form));

	for(i=0,j=0;i<strlen(form);i++)
		{
		switch(form[i])
			{
  			case 'f':
			case 't': 
				for(str=instr;!!*str;str++)
					if(*str==' ')
						break;
				strncpy(defn+j,instr,str-instr);
				j+=str-instr;
				*(defn+j)=' ';j++;
				while(*str==' ')
					str++;
				refresh_buffer(instr,str-instr,64);
				break;		
			case 'x':
			case 'l':
				if(*instr=='(')
					{
					char vname[34];
					int chg=0;
					str=instr+1;
					while(*str==' ')
						str++; 
					refresh_buffer(instr,str-instr,64);
					str=get_variable(instr);
					while(*str==' ') str++;

					if(*str==')')
					  {
					    struct vardecl_list * tvlist=NULL;
					    str--;
					    while(*str==' ') str++;
					    while(isidch(*str)) str--;
					    str++;
					    tvlist=tmpvarll.vlist;
					    tmpvarll.vlist=my_malloc(sizeof(struct vardecl_list));
					    tmpvarll.vlist->next=tvlist;
					    tmpvarll.vlist->var=my_malloc(sizeof(struct var_decl));
					    refresh_buffer(instr,str-instr,64);
					    str=get_variable(instr);
					    memset(vname,0,sizeof(vname));
					    strncpy(vname,instr,str-instr);
					    tmpvarll.vlist->var->name=strdup(vname);
					  }
					else
					  {
					    str=instr;
					    tmpvarll.vlist=opvars;

					    if(!get_var_decl(instr,types,&tmpvarll,NULL,fnlist,op,iop,NULL,9,&chg))
					      {
						fprintf(stderr,"get_opdef_def(): can't get var from code:%s\n",instr);
						return 0;
					      };
					    str=instr+chg;
					  
					    memset(vname,0,sizeof(vname));
					    if(*str==')')
					      str--;
					    while(*str==' ')
					      str--;
					    while(isidch(*str))
					      str--;
					    str++;
					    
					    if(!isidch(*str))
					      {
						fprintf(stderr,"get_opdef_def(): can't get var from code:%s\n",instr);
						return 0;
					      };
					    refresh_buffer(instr,str-instr,64);
					    str=get_variable(instr);
					    strncpy(vname,instr,str-instr);
					  };
				
					    opvars=tmpvarll.vlist;
				
					    while(*str==' ')
					      str++; 
					    if(*str!=')')
					      {
						fprintf(stderr,"get_opdef_def(): no ')' in op definition !\n");
						return 0;
					      };
					    str++;
					    while(*str==' ')
					      str++; 
					    refresh_buffer(instr,str-instr,64);
				
					    strcpy(defn+j,vname);
					    j+=strlen(vname);
					    *(defn+j)=' ';j++;
				break;		
			};

				for(str=instr;!!*str;str++)
					if(*str==' ')
						break;
				strncpy(defn+j,instr,str-instr);
				j+=str-instr;
				*(defn+j)=' ';j++;
				while(*str==' ')
					str++;
				refresh_buffer(instr,str-instr,64);
				break;
			case 'c':
				if(mode!=2)
					{
					fprintf(stderr,"\'c\' format specifier in definition of non-keyword operator !\n");
					return 0;
					};
				for(str=instr;!!*str;str++)
					if(*str==' ')
						break;
				strncpy(defn+j,instr,str-instr);
				j+=str-instr;
				*(defn+j)=' ';j++;
				while(*str==' ')
					str++;
				refresh_buffer(instr,str-instr,64);
				break;		
			default:
				fprintf(stderr,"Unknown \'%c\' character in format specifier in definition of operator !\n",form[i]);
				return 0;
			};
		};
	str=instr;
	tmp=defn;
	defn=strdup(tmp); 
	free(tmp);
	/* to avoid wasting memory we realoccate it to the size of the string */

	res=my_malloc(sizeof(struct op_definition));
	res->attr=OPD_NONE;
	res->prec=prec;
	res->defn=defn;
	res->form=strdup(form);
	res->vars=opvars;

	ltmp=mk_head_lnd(NULL,NULL,0);

	if(mode==1)
		{
		str+=get_expression(instr,ltmp,op,iop,0,types,&tmpvarll,fnlist);
		if(str==instr)
			{
			fprintf(stderr,"get_opdef_def(): error getting expression for op def !\n");
			return 0;
			};
		if((ltmp=op_to_function(rm_cdr_next(ltmp)))==NULL)
			{
			fprintf(stderr,"get_opdef_def(): error getting expression for op def !\n");
			return 0;
			};
		res->fdef.opdef=ltmp;
		while(*str==' ')
			str++; 
		refresh_buffer(instr,str-instr,64);
		opdef_ins(res,op);
		}
	else
		{
		listnode llab=NULL,subfunc=NULL;
		subfunc=mk_head_lnd(iop,my_malloc(sizeof(struct op_definition)),INLINSUB);
		((defined_op)subfunc->data)->attr=STOP_XOP;

		llab=mk_head_lnd(NULL,mk_head_lnd(NULL,NULL,LABELDEF),TWOLISTS);

		if(!get_code_block(instr,ltmp,subfunc,op,NULL,llab,&tmpvarll,types,fnlist,1))
			{
			fprintf(stderr,"get_opdef_def(): error getting code block for inline op def !\n");
			return 0;
			};

		res->fdef.inlfun=my_malloc(sizeof(struct inline_fun_rec));
		res->fdef.inlfun->args=opvars;
		res->fdef.inlfun->body=rm_cdr_next(ltmp);

		if(llab->next!=NULL)
			res->fdef.inlfun->labels=llab;
		else if(llab->data==NULL)
			res->fdef.inlfun->labels=NULL;
		else if(llab->attr!=TWOLISTS)
			res->fdef.inlfun->labels=llab;
		else if(((listnode)llab->data)->next!=NULL)
			res->fdef.inlfun->labels=llab;
		else if(((listnode)llab->data)->data==NULL)
			res->fdef.inlfun->labels=NULL;
		else
			res->fdef.inlfun->labels=llab;

		if(((defined_op)subfunc->data)->attr!=STOP_XOP)
			res->fdef.inlfun->subfunctions=(defined_op)subfunc->data;
		else
			res->fdef.inlfun->subfunctions=NULL;

		opdef_ins(res,(defined_op)iop->data);
		};	
	return 1;
	}
