/*
 * Copyright (C) Andrew Wuensche
 *
 *  This file is part of DDLab (Discrete Dynamics Lab) <www.ddlab.org>
 *
 *  DDLab 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 DDLab.  If not, see <http://www.gnu.org/licenses/>.
 */

/********  orbants  ************/
#   include "edecfunc.h"

/*void new_ant(void);*/
void draw_bottom_title_ants(void);

/********  orbants  ************/
void new_ant(void){

   unsigned int auto_pause=10;
   char test,exitwhile,trailcolor,
     tooclose,pause_at_end=0;
   signed int Tmax,start_Tmax,z,closest,target_diam;
   float dx,dy,
	 dxant,dyant,
	 x,y,
	 oldx,oldy,
	 *xt,*yt,*distance,*angdiff,
         angle,*heading,
	 newangdiff,
	 ratio,
         turnrate,fast_turnrate,normal_turnrate,
         adiff,dis,head;
   

   unsigned int wcount,zu,speed;;
        draw_bottom_title_ants();

	/*initial ant position and angle*/
	x=Maxx/2;
	y=Maxy/2;
	angle=deg*100;
        //Maxul=-1;
        speed=Maxu/(256*32);       
        if (speed<256) speed=256;
        
	Tmax=25; /*initial number of targets*/

      newsetup: 
        target_diam=vgax(5);
        /*allocate memory*/
        if ((xt=(float*)calloc(Tmax+1,
                   sizeof(float)))==NULL) outof_memory("xt"); 
        if ((yt=(float*)calloc(Tmax+1,
                   sizeof(float)))==NULL) outof_memory("yt");
        if ((distance=(float*)calloc(Tmax+1,
                   sizeof(float)))==NULL) outof_memory("distance");
        if ((angdiff=(float*)calloc(Tmax+1,
                   sizeof(float)))==NULL) outof_memory("angdiff");
        if ((heading=(float*)calloc(Tmax+1,
                   sizeof(float)))==NULL) outof_memory("heading");
       
        /*gravitational pull of target*/
	normal_turnrate=turnrate=.5;
        fast_turnrate=.75;

      same_setup:
        start_Tmax=Tmax;
	for (z=0;z<=Tmax;z++){
	   xt[z]=vgax(20)+rand()%(Maxx-vgax(40));
	   yt[z]=vgay(20)+rand()%(Maxy-vgay(40));
	}
	
	/*white border around screen*/
	setcolor(white);
	rectangle(0,0,Maxx,Maxy);
	setviewport(1,1,getmaxx()-1,Maxy-1,1);
	/*restart new ants*/
       start:
	if (pause_at_end==1){ /*pause between sessions*/
           while (!event_ready());
        }
	/*set different trail colours at random*/
	  /*except black, white*/
	while(1){
	   trailcolor=rand()%16;
	   if (trailcolor>1 && trailcolor!=white) break;
	}
        //fprintf(stderr,"\nspeed=%d",speed);
	wcount=0; /*count ant steps*/
        exitwhile=0;

	setcolor(white);

	/*clear screan for each new ants*/
	clearviewport();
        rectangle(0,0,Maxx,Maxy);
        draw_bottom_title_ants();

	/*re-draw targets*/
	setfillstyle(SOLID_FILL,0);
	for (z=0;z<=Tmax;z++){
           setfillstyle(SOLID_FILL,yellow);
           pieslice(1,(signed int)xt[z],(signed int)yt[z],
                                              0,360,target_diam);            
	}

	while(1){
	       wcount+=1;
               if (wcount==Maxu) wcount=2;
	       if (event_ready()){    /*if a key is hit.....*/
		   test=is[0];
		   switch(test){
                        case '*':
                         /*gravitational pull of target*/
	                 normal_turnrate=turnrate=.5;
                         fast_turnrate=.75;
                         break; 
		      case '+':
                        if (turnrate<2){
                           turnrate*=1.5;
                           normal_turnrate*=1.5;
                           fast_turnrate*=1.5;
                        }
                        break; 
                      case '-':
                        turnrate*=.75;
                        normal_turnrate*=.75;
                        fast_turnrate*=.75;
                        break; 
		      case '>': /*faster*/
			speed=speed/2;
                        if (speed<=1) speed=1;
                        break; 
                      case '<':  /*slower*/
			speed=speed*2;
                        if (speed>=Maxu/256) speed=Maxu/258;
                        break;      
		      case 'q':
                         /*key 'q' - exit ants*/
			 exitwhile=1;
			 break;		         
		      case 'p':
                         /*key 'p' - pause until another key press
		         this also allows ant to take single steps*/
			 while (!event_ready());
			 break;
		      case 'P': 
                         pause_at_end=abs(pause_at_end-1);
			 break;
		      case 'r':
                         /*key 'r' repositions the targets at random
		         keep same ant to chase new targets
			 delete mark in old closest target*/
			 setcolor(black);
			 /*delete old targets*/
			 for (z=0;z<=Tmax;z++){
                            setfillstyle(SOLID_FILL,black);
                            pieslice(1,(signed int)xt[z],
                                      (signed int)yt[z],
                                        0,360,target_diam); 
                         }
			 /*set new random target positions*/
			 for (z=0;z<=Tmax;z++){
			    xt[z]=vgax(20)+rand()%(Maxx-vgax(40));
			    yt[z]=vgay(20)+rand()%(Maxy-vgay(40));
			 }
			 /*draw new targets*/
			 setcolor(white);
			 for (z=0;z<=Tmax;z++){
                            setfillstyle(SOLID_FILL,yellow);
                            pieslice(1,(signed int)xt[z],
                                     (signed int)yt[z],
                                     0,360,target_diam); 
			 }
			 /*setlinestyle(SOLID_LINE,1,THICK_WIDTH);*/
			 break;
		      case 't':
			 /*key 't' to reposition closest target only*/
			 /*delete closest tartget*/
                         setcolor(black);
                         setfillstyle(SOLID_FILL,black);
                         pieslice(1,(signed int)xt[closest],
                                    (signed int)yt[closest],
                                      0,360,target_diam); 
			 /*set new position*/
			 xt[closest]=20+rand()%(Maxx-vgax(40));
			 yt[closest]=20+rand()%(Maxy-vgay(40));
			 /*draw new target*/
			 setcolor(white);
			 setfillstyle(SOLID_FILL,yellow);
                         pieslice(1,(signed int)xt[closest],
                                     (signed int)yt[closest],
                                       0,360,target_diam);
			 break;
		      
		      case 'a':
			 /*key 'a' create a new ant at new poss/angle
                           but not too close to a target*/
			 while(1){
			    x=rand()%(Maxx-vgax(40));
			    y=rand()%(Maxy-vgay(40));
			    angle=deg*(rand()%360);

			    tooclose=0;
			    for (z=0;z<=Tmax;z++){
			      distance[z]=
				pow((pow(abs(x-xt[z]),2)+
					 pow(abs(y-yt[z]),2)),.5);
			      if (distance[z]<10){
				 tooclose=1;
				 break;
			      }
			    }
			    if (tooclose==0) break;
			 }
			 goto start;			
		       case 'i':
			 /*increase number of targets by 1*/
			  if (Tmax<15){
			    /*delete mark in old closest target*/
			    if (wcount>1) putpixel(xt[closest],yt[closest],yellow);
			    Tmax+=1;                           
			    xt[Tmax]=vgax(20)+rand()%(Maxx-vgax(40));
			    yt[Tmax]=vgay(20)+rand()%(Maxy-vgay(40));
			    /*draw new target*/
			    setcolor(white);
                            setfillstyle(SOLID_FILL,yellow);
                            pieslice(1,(signed int)xt[Tmax],
                                      (signed int)yt[Tmax],
                                        0,360,target_diam); 
			  }
			  break;
			  
		       case 'd':
			  /*decrease number of targets by 1*/
			  if (Tmax>0){
			    /*delete mark in old closest target*/
                            setcolor(white);
                            setfillstyle(SOLID_FILL,yellow);
                            pieslice(1,(signed int)xt[closest],
                                       (signed int)yt[closest],
                                        0,360,target_diam); 
                            /*delete Tmax target*/
			    setcolor(black);		    
                            setfillstyle(SOLID_FILL,black);
                            pieslice(1,(signed int)xt[Tmax],
                                      (signed int)yt[Tmax],
                                        0,360,target_diam); 
			    Tmax-=1;
			  }
			  break;
		       case 'c':
                         /*clear the screen - redraw targets
			   and continue with the same ant*/
			 clearviewport();
                         draw_bottom_title_ants();
			 goto start;
		   }
		}
		if (exitwhile==1) break;

		setcolor(black);
		if (wcount>1){
		   /*delete ant*/
                  thickline(-1,oldx,oldy,oldx+dxant*vgax(8),oldy+dyant*vgay(8),
                                        black,vgax(4),0);
		  /*set multicolor trail*/
		   putpixel(oldx,oldy,trailcolor); /*for red trail only - red*/
                   putpixel(oldx+1,oldy,trailcolor);
                   putpixel(oldx,oldy+1,trailcolor);
                   putpixel(oldx+1,oldy+1,trailcolor);
		}
		oldx=x;
		oldy=y;

		/*distance of ant from each target*/
		for (z=0;z<=Tmax;z++) distance[z]=
			pow((pow(abs(x-xt[z]),2)+pow(abs(y-yt[z]),2)),.5);

		/*ant moves forward (acc to where it points)
		  by the equivalent of one pixel, by step dx,dy*/
		/*top left*/
		if(angle>=0 && angle<=PI/2){
		    dx=cos(angle);
		    dy=-sin(angle);
		}
		/*bot left*/
		else if(angle>=PI/2 && angle<=PI){
		    dx=-sin(angle-PI/2);
		    dy=-cos(angle-PI/2);
		}
		/*bot right*/
		else if (angle>=PI && angle<=1.5*PI){
		    dx=-cos(angle-PI);
		    dy=sin(angle-PI);
		}
		/*top right*/
		else if (angle>=1.5*PI && angle<=2*PI){
		    dx=sin(angle-1.5*PI);
		    dy=cos(angle-1.5*PI);
		}
		/*save ant step*/
		dxant=dx;
		dyant=dy;

		/*slow ant down close to a target (by reducing step size)
		if angle between ant direction and heading to a target
		is close to 90 degrees.
		This causes greater turn ("descent") toward a target*/
		for (z=0;z<=Tmax;z++){
		   if (distance[z]<vgax(70)
			&& angdiff[z]>80*deg && angdiff[z]<100*deg){
		      dx*=distance[z]/((angdiff[z]/deg));
		      dy*=distance[z]/((angdiff[z]/deg));
		   }
		}
		/*draw ant as thick red? line*/
                thickline(-1,x,y,x+dxant*vgax(8),y+dyant*vgay(8),
                                             lightred,vgax(4),0);
                
		x+=dx;
		y+=dy;

		/*calculate heading from ant to each target,
		and absolute angle between ant direction and heading*/
		for (z=0;z<=Tmax;z++){
		    if (x!=xt[z]){
		       ratio=fabs((double)(y-yt[z])/(double)(x-xt[z]));
		       heading[z]= (float) atan (ratio);
		       if (y>yt[z] && x>xt[z]) heading[z]=PI-heading[z];
		       else if (y<yt[z] && x>xt[z]) heading[z]=PI+heading[z];
		       else if (y<=yt[z] && x<xt[z])
					      heading[z]=2*PI-heading[z];
		    }
		    else if (x==xt[z] && y<yt[z]) heading[z]=3*PI/2;
		    else if (x==xt[z] && y>yt[z]) heading[z]=PI/2;

		    /*absolute angle between ant direction
		      and heading to each target*/
		    angdiff[z]=fabs(heading[z]-angle);
		    if (angdiff[z]>PI) angdiff[z]=2*PI-angdiff[z];
		}
		/*delete mark in old closest target*/
		 if (wcount>1){
                    setcolor(white);
                    setfillstyle(SOLID_FILL,yellow);
                    pieslice(1,(signed int)xt[closest],
                               (signed int)yt[closest],
                                 0,360,target_diam); 
                 }
		 /*find closest target*/
		 closest=0;
		 for (z=1;z<=Tmax;z++){
		    if (distance[z]<distance[closest]) closest=z;
		 }
		 /*mark new closest target with dot*/
		 if (wcount>1){
                    setcolor(black);		    
                    setfillstyle(SOLID_FILL,lightred);
                    pieslice(1,(signed int)xt[closest],
                               (signed int)yt[closest],
                                   0,360,target_diam/2); 
                 }
                 /*to avoid getting stuck in orbit*/
                 if (wcount>=10000) turnrate=fast_turnrate;
                 else turnrate=normal_turnrate;
                
		 /*minimize the absolute angle difference to closest target*/
		 adiff=angdiff[closest];
		 dis=distance[closest];
		 head=heading[closest];

		 /*change the angle the ant is pointing
		 turnrate=turn constant
		 bigger angle difference - bigger turn
		 bigger distance - smaller turn*/

		 /*try left turn*/
		 angle-=turnrate*deg*log((adiff/deg))/log(dis);
		 if (angle==2*PI) angle=0;
		 else if (angle>2*PI) angle-=2*PI;
		 else if (angle<0) angle=2*PI-angle;
		 newangdiff=fabs(head-angle);
		 if (newangdiff>PI) newangdiff=2*PI-newangdiff;

		 /*if left turn increases the absolute angle difference
		   turn right but twice the turn angle*/
		 if (newangdiff>adiff){
		   angle+=2*turnrate*deg*log((adiff/deg))/log(dis);
		 /*if (newangdiff<adiff){  alternatively, turn away
		   angle-=2*turnrate*deg*log((adiff/deg))/log(dis);*/
		    if (angle==2*PI) angle=0;
		    else if (angle>2*PI) angle-=2*PI;
		    else if (angle<0) angle=2*PI-angle;
		 }                 
                 angdiff[closest]=newangdiff;

		 /*if ant reaches target, kill the target
		 retset target index of other target
		 if ness. For last target - reset 3 targets and restart*/
		 if (distance[closest]<target_diam/2){
		    wcount=2;/*reset count*/
		    if (Tmax>0){
                       /*delete old closest target*/
		       setcolor(black);	       
                       setfillstyle(SOLID_FILL,black);
                       pieslice(1,(signed int)xt[closest],
                                  (signed int)yt[closest],
				  0,360,target_diam-1);
                       /*change trail color after each hit*/
                        trailcolor+=1;
                        if (trailcolor==16) trailcolor=2;
                        //fprintf(stderr," %d",trailcolor);
		    }
                    

		    if (closest<Tmax){
		       for (z=closest+1;z<=Tmax;z++){
			  xt[z-1]=xt[z];
			  yt[z-1]=yt[z];
		       }
		    }
		    Tmax-=1;
                    /*find new closest target*/
		    closest=0;
		    for (z=1;z<=Tmax;z++){
		       if (distance[z]<distance[closest]) closest=z;
		    }
		    /*mark new closest target with dot*/
		    if (wcount>1){
                       setcolor(black);		    
                       setfillstyle(SOLID_FILL,lightred);
                       pieslice(1,(signed int)xt[closest],
                                  (signed int)yt[closest],
                                   0,360,target_diam/2); 
                    }
		    if (Tmax==-1){
		       Tmax=start_Tmax;
                       for (zu=0;zu<=(Maxu-1)/auto_pause;zu++);
                       goto same_setup;
                    }
		 }

		 /*bounce off walls*/
		if (x<=1 || x>=Maxx-1){
                   if (x<=1) x=2;
                   if (x>=Maxx-1) x=Maxx-2;                   
		   angle=3*PI-angle;
		   if (angle==2*PI) angle=0;
		   else if (angle>2*PI) angle-=2*PI;
		   else if (angle<0) angle=2*PI-angle;
                   sprintf(textb[0],"%.2f",angle); /*"nan" = infinite?*/
                   if (memcmp(textb[0],"nan",strlen(textb[0]))==0 )  
		                   angle=deg*(rand()%360);
                   
		}
		else if (y<=1 || y>=Maxy-1){
		   if (y<=1) y=2;
                   if (y>=Maxy-1) y=Maxy-2;
		   angle=2*PI-angle;
		   if (angle==2*PI) angle=0;
		   else if (angle>2*PI) angle-=2*PI;
		   else if (angle<0) angle=2*PI-angle;
                   sprintf(textb[0],"%.2f",angle);/*"nan" = infinite?*/
                   if (memcmp(textb[0],"nan",strlen(textb[0]))==0 )  
                                       angle=deg*(rand()%360);                
		} 
               
                /*slow down*/
		for (zu=0;zu<=speed;zu++);            
        }

        draw_prompt_box(1);
        setcolor(white);
        outtext("quit-q options-o restart-r:");
        input_string(1,0);
        if (is[0]=='r'){            
	   erase_prompt_box(1);
           goto start;
        } 
        else if (is[0]=='o'){
           draw_prompt_box(1);
           sprintf(textb[0],"reset auto-pause (slow=1 now %d ):",auto_pause);
           outtext(textb[0]);
           input_string(5,0);
           if (is[0]!=13 && atoi(is)>0) auto_pause=atoi(is);

           sprintf(textb[0]," reset target number (now %d):",Tmax);
           outtext(textb[0]);
           input_string(5,0);
           if (is[0]!=13 && atoi(is)>0){
              Tmax=atoi(is);
              free(xt);
              free(yt);
              free(distance); 
              free(angdiff);
              free(heading);
              goto newsetup;
	   } 
           else goto start;
        } 

        free(xt);
        free(yt);
        free(distance); 
        free(angdiff);
        free(heading);
}  

/********************** draw bottom title for ants, Jan 94 **********************/
void draw_bottom_title_ants(void){
  
#if __WATCOM__
    signed int CHeight_t,CWidth_t;
    signed int hmove,vmove,tempfont;

	tempfont=sfont;
	setviewport(0,0,Maxx,getmaxy(),0);
	setfillstyle(SOLID_FILL,black);
	bar3d(0,getmaxy()-vgay(13),Maxx,getmaxy(),0,0);
	setfillstyle(CLOSE_DOT_FILL,darkgray);
	bar3d(0,getmaxy()-vgay(13),Maxx,getmaxy(),0,0);
	setcolor(white);
	line(0,getmaxy()-vgay(13),Maxx,getmaxy()-vgay(13));
	moveto(3,getmaxy()-vgay(12));
	setcolor(yellow);

#       if __WATCOM__
	     if (hires==2){
		if (sfont>=1) sfont=5;
	     }
	     else if (hires==1){
		if (sfont>=1) sfont=6;
	     }
	      else if (hires==0){
		if (sfont>=1) sfont=2;
	     }
	     if (sfont>=1){  /*if font being used*/
		sprintf(textb[0],"n%d",sfont);
		_setfont(textb[0]);
		_getfontinfo(&info);
		CHeight_t=info.pixheight;
		CWidth_t=info.avgwidth;
	     }
	     else{   /*for line segment font*/
		 _setcharsize(tyd,txd);
		 _gettextsettings(&ts);
		 CWidth_t=ts.width;
		 CHeight_t=ts.height;
	     }
	     if (hires==2 || hires==1){
		hmove=-CWidth_t*.5;
		vmove=CHeight_t*.6;
	     }
	      else if (hires==0){
		hmove=-CWidth_t*.6;
		vmove=CHeight_t*.65;
	     }
	     outtext("ORBITAL ANTS");
	     outtext("  c");
	     moverel(hmove,vmove);
	     circle(getx,gety,vgax(4.5));
	     moverel(CWidth_t,-vmove);
	     outtext(" 1996-2012");

#       endif
	/*moveto(Maxx/2,gety);*/
	setcolor(lightred);
        outtext(
                "  exit-q speed-</> clear-c pause-p endpause-P newant-a turn+/*/-");
	outtext(" targets: closest-t all-r inc-i dec-d");
	setcolor(white);
	setfillstyle(SOLID_FILL,black);
	setviewport(0,0,Maxx,Maxy,1);

#      if __WATCOM__
	     if (sfont>=1){  /*if font being used*/
		sfont=tempfont;
		sprintf(textb[0],"n%d",sfont);
		_setfont(textb[0]);
		_getfontinfo(&info);
		CHeight_t=info.pixheight;
		CWidth_t=info.avgwidth;
	     }
	     else{   /*for line segment font*/
		 _setcharsize(ty1,tx1);
		 _gettextsettings(&ts);
		 CWidth_t=ts.width;
		 CHeight_t=ts.height;
	     }
#         endif

    /******************** UNIX code ********************/
#   elif __UNIX__
    signed int tempx,tempy;

	tempx=getx;
	tempy=gety;
       
        setviewport(0,Maxy+1,Maxx,WinMaxy,0);

	setfillstyle(SOLID_FILL,black);    
	bar(0,Maxy+1,Maxx,WinMaxy);
        
	setcolor(lightred);
	rectangle(0,Maxy+1,Maxx,WinMaxy);
	moveto(vgax(3),WinMaxy-vgay(12));
	setcolor(yellow);

	XSetFont(D,Gc,italicfont->fid);
	outtext("ORBITAL ANTS  ");
	circle(getx+2,gety+CHeight*.6,CWidth*.8);
	outtext("c");
	outtext("  1996-2012");
        
        /*restor default font*/  
        XSetFont(D,Gc,propfont->fid);    
        setcolor(lightred);
        outtext(
                "  exit-q speed-</> clear-c pause-p endpause-P newant-a turn+/*/-");
	outtext(" targets: closest-t all-r inc-i dec-d");
	
        setcolor(white);
	setviewport(0,0,Maxx,Maxy,1);                
        moveto(tempx,tempy);
        
#   endif
}







