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

struct typerec PTR apply_modifier(
struct typerec PTR type,
listnode modif,
listnode * std_constr,
struct type_listoflist PTR types)
{
   int applied_flag=0; 
   /* 0-wasn't applied,1-modif was applied to base type (first arg)*/
   listnode l,l1;
   listnode mconstr=NULL;
   struct typerec PTR res=NULL;
   
   if(modif==NULL)
     return type;
   if(type==NULL)
     return NULL;
   
   for(l=modif;l!=NULL;l=l->next)
     {
	if((l->attr!=TYPEDEFN)&(l->attr!=TWOLISTS))
	  fprintf(stderr,"apply_modifier():l->attr!=TYPEDEFN or TWOLISTS !\n");
	else if(l->data==NULL)
	  fprintf(stderr,"apply_modifier():l->data==NULL !\n");
	else if(((struct typerec PTR)((l->attr==TWOLISTS)?((listnode)l->data):l)->data)->typekind!=ISMODIF)
	  fprintf(stderr,"apply_modifier():type %s not modifier !\n",((struct typerec PTR)l->data)->name);
	else
	  {
	     struct type_list PTR tl,PTR tl1;
	     if(l->attr==TYPEDEFN)
	       l1=l;
	     else
	       l1=(listnode)l->data;
	     
	     while(1) 
	     /* to check modifier arg types and try 
	      to find alternate declaration if not */
	       {
		  int type_matches=0;
		  /* 0 - not checked yet, 1 - main (first) arg matches,
		   2 - all fine, 3 - no match, 4 - only one type 
		   passed as arg*/
		  for(tl=((struct typerec *)l1->data)->typebody.class_def->types;tl!=NULL;tl=tl->next)
		    {  
		       tl1=NULL;
		       if(tl->type!=NULL)
			 if(tl->type->typekind==MODIFARG)
			 {
			    if(type_matches==4)
			      {
				 type_matches=3;
				 break;
			      }
			    else
			      tl1=tl->type->typebody.class_list;
			    if(type_matches==1)
			      while(l1->attr==TWOLISTS)
				l1=l1->next;
			    if(l1==NULL)
			      {
				 type_matches=3;
				 break;
			      };
			 };
		       while(tl1!=NULL)
			 {
			    if(tl1->type!=NULL)

			      if(!check_top_class((!type_matches)?type:(struct typerec *)l1->data,tl1->type->typeid))
			      {
				 type_matches=3;
				 break;
			      };
			    tl1=tl1->next;
			 };
		       if(type_matches==3)
			 break;
		       if(!type_matches) 
			 /* first time */
			 {
			    if(l->attr==TYPEDEFN)
			      type_matches=4;
			    else
			      {
			      type_matches=1;
			      l1=l->next;
			      };
			 };
		    };
		  if(type_matches!=3)
		    break; /* everything is fine, let's apply modif */

		  if(l->attr==TYPEDEFN)
		    l1=l;
		  else
		    l1=(listnode)l->data;
		  res=(struct typerec *)l1->data;
		  l1->data=(char *)get_type_by_name_skip(res->typeid,res->name,res->typemodf,types);
		  if(l1->data==NULL) /* no modifiers left ! */
		    {
		       fprintf(stderr,"apply_modifier():no matching modifiers !\nTrying to recover ...");
		       return type;
		    };
	       };
	     mconstr=NULL;	     
	     if(l->attr==TWOLISTS)
	       if(((listnode)l->data)->attr!=TYPEDEFN)
		 fprintf(stderr,"apply_modifier():l->data->attr!=TYPEDEFN!\n");
	     res=my_malloc(sizeof(struct typerec));
	     res->name=Strdup(type->name);
	     if(l->attr==TYPEDEFN)
	       res->size=type->size+((struct typerec PTR)l->data)->size;
	     else
	       res->size=type->size+((struct typerec PTR)((listnode)l->data)->data)->size;				
	     res->typekind=CLASS;
	     res->typemodf=MODIFIED;
	     res->typebody.class_def=my_malloc(sizeof(struct class_def));
	     res->typebody.class_def->top_class=my_malloc(sizeof(struct type_list));
	     res->typebody.class_def->top_class->next=NULL;
	     
	     if(l->attr==TYPEDEFN)
	       res->typebody.class_def->top_class->type=(struct typerec PTR)l->data;
	     else
	       res->typebody.class_def->top_class->type=(struct typerec PTR)((listnode)l->data)->data;
	     
	     res->typebody.class_def->top_class->convert_lock=OFF;
	     res->typebody.class_def->types=NULL;
	     if(l->attr==TYPEDEFN)
	       tl=((struct typerec PTR)l->data)->typebody.class_def->types;
	     else
	       tl=((struct typerec PTR)((listnode)l->data)->data)->typebody.class_def->types;
	     if(tl==NULL)
	       fprintf(stderr,"Weird - modifier w/o arguments\n");
	     l1=NULL;
	     while(tl!=NULL)
	       {
		  if(tl->type!=NULL)
		    {
		       tl1=res->typebody.class_def->types;
		       res->typebody.class_def->types=my_malloc(sizeof(struct type_list));
		       res->typebody.class_def->types->next=tl1;
		       if(tl->type->typekind==MODIFARG)
			 {					
			    res->typebody.class_def->types->type=my_malloc(sizeof(struct typerec));
			    *res->typebody.class_def->types->type=*tl->type;
			    res->typebody.class_def->types->type->name=Strdup(tl->type->name);
			    res->typebody.class_def->types->type->typekind=TYPEREFR;
			    if(!applied_flag)
			      {
				 res->typebody.class_def->types->type->typebody.pointer_to=type;
				 applied_flag=1;							
				 
				 if(l->attr==TYPEDEFN)
				   break;
				 else
				   l1=((listnode)l->data)->next;
			      }
			    else
			      {
				 if(l1==NULL)
				   {
				      fprintf(stderr,"apply_modifier(): In modif arg type list l1==NULL !\n");							
				      break;
				   };
				 if(l1->attr==TWOLISTS)
				   {
				      if(mconstr==NULL)
					{
					   mconstr=(listnode)l1->data;
					   l1=l1->next;
					}
				      else
					fprintf(stderr,"Double constructor call at modifier !!!\n");
				   };
				 
				 if(l1==NULL)
				   {
				      fprintf(stderr,"apply_modifier(): In modif arg type list l1==NULL !\n");
				      break;
				   };
				 if(l1->attr!=TYPEDEFN)
				   fprintf(stderr,"apply_modifier(): In modif arg type list attr!=TYPEDEFN !\n");	
				 
				 res->typebody.class_def->types->type->typebody.pointer_to=(struct typerec PTR)l1->data;
				 l1=l1->next;
			      };
			 }
		       else 
			 /* if this is not a modif arg we still need to copy it because modifier's
			  type list is ignored when validating function */
			 res->typebody.class_def->types->type=tl->type;
		    };
		  tl=tl->next;
	       }
	     if(l1!=NULL)
	       if(l1->attr==TWOLISTS)
	       {
		  if(mconstr==NULL)
		    mconstr=(listnode)l1->data;
		  else
		    fprintf(stderr,"Double constructor call at modifier !!!\n");
	       };
	     if(std_constr!=NULL)
	       {
		  res->typebody.class_def->variable_constructors=*std_constr;
		  *std_constr=mconstr;
	       };
	     type=res; /* we may have other modifiers to apply */
	  };
     };
   return type;
}

int check_top_class(
struct typerec PTR type,
int top_cl_id)
/* checks wether type with id top_cl_id is a top class of "type" */
{
struct type_list PTR tl;

   if(type==NULL)
     return 0;
   if(type->typeid==top_cl_id)
     return 1;
   while((type->typekind==TYPEREFR)&(type->typemodf==NONE))
     type=type->typebody.pointer_to;
   if(type->typeid==top_cl_id)
     return 1;
   if((type->typekind!=CLASS)&(type->typekind!=ISMODIF)&(type->typekind!=MODIFARG))
     return 0;
   
   tl=(type->typekind==MODIFARG)?type->typebody.class_list:type->typebody.class_def->top_class;
   while(tl!=NULL)
     {
	if(tl->type!=NULL)
	  if(tl->type->typeid==top_cl_id)
	    return 1;
	if(check_top_class(tl->type,top_cl_id))
	  return 1;
	else
	  tl=tl->next;
     }
   return 0;
}

struct typerec PTR get_type_by_name(char PTR name,enum typemodf tm, struct type_listoflist PTR tll)
{
struct type_list PTR tl;

while(tll!=NULL)
	{
	tl=tll->tlist;
	while(tl!=NULL)
		{        /* if "name" == NULL then look only on modifier-used to find modifiers types */
		if(tl->type!=NULL)
				if(((name==NULL)?1:!Strcmp(name,tl->type->name))&(tl->type->typemodf==tm))
					break;
		tl=tl->next;
		};
	if(tl!=NULL)
		break;
	tll=tll->next;
	};

if (tl==NULL)
	return NULL;
return tl->type;
}


struct typerec PTR get_type_by_name_skip(long skip_id,char PTR name,enum typemodf tm, struct type_listoflist PTR tll)
{
struct type_list PTR tl;

   while(tll!=NULL)
     {
	tl=tll->tlist;
	while(tl!=NULL)
	  {        /* if "name" == NULL then look only on modifier-used to find modifiers types */
	     if(tl->type!=NULL)
	       if(((name==NULL)?1:!Strcmp(name,tl->type->name))&(tl->type->typemodf==tm))
	       {
		  if(!skip_id)
		    break;
		  else
		    if(tl->type->typeid==skip_id)
		      skip_id=0;
	       };
	     tl=tl->next;
	  };
	if(tl!=NULL)
	  break;
	tll=tll->next;
     };
   
   if (tl==NULL)
     return NULL;
   return tl->type;
}



void PTR set_typerec(
struct typerec PTR tr,
int size,
char PTR name,
enum typekind typekind,
enum typemodf typemodf,
enum tb_built_in builtintb,
struct fundef_list PTR typeconv)
{
tr->size=size;
tr->name=name;
tr->typekind=typekind;
tr->typemodf=typemodf;
tr->typebody.built_in=builtintb;
tr->typeconv=typeconv;
return tr;
}

struct type_list PTR install_type(
struct type_list PTR tl,
int size,
char PTR name,
enum typekind typekind,
enum typemodf typemodf,
enum tb_built_in typebody,
struct fundef_list PTR typeconv)
{
while(tl->next!=NULL)
	{
	if(tl->next->type!=NULL)
		if((!Strcmp(name,tl->next->type->name))&(tl->next->type->typemodf==typemodf))
			{
			   if(typekind!=ISMODIF) 
			     /* there can be more than one modif with same name */
			     { 
				printf("Type %s already exist !!! Replacing ...\n",name);
				break;
			     };
			};
	tl=tl->next;
	};
if(tl->next==NULL)
	{
	tl->next=my_malloc(sizeof(struct type_list));
	tl->next->next=NULL;
	tl->next->type=my_malloc(sizeof(struct typerec));
	};

set_typerec(tl->next->type,size,name,typekind,typemodf,typebody,typeconv);
global_type_id_count++;
tl->next->type->typeid=global_type_id_count;
tl->next->convert_lock=OFF;
return tl->next;
}





