/* 
 * Thomas Pantzer: t_runtime_obj.cc, v0.4.7  8.6.99 16:32 
 * 
 * 
 * Copyright 1999 by Thomas Pantzer
 * 
 * Permission to use, copy, modify, and distribute this software for noncommercial 
 * use and without fee is hereby granted, provided that the above copyright notice 
 * appear in all copies and that both that copyright notice and this permission 
 * notice appear in supporting documentation, and that the name of the Author not be 
 * used in advertising or publicity pertaining to distribution of the software 
 * without specific, written prior permission. The Author makes no representations about
 * the suitability of this software for any purpose.  It is provided "as is" 
 * without expressed or implied warranty. 
 * 
 * 
 * pantec@isys2.informatik.uni-leipzig.de   (Thomas Pantzer) 
 * 
 */




#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#include "copyright.h"

#include "t_runtime_obj.h"
#include "stuff.h"


void t_runtime_root::Init(void)
{
	state = _s_undefined;

	for (int i=0; i<MAX_RUNTIME_OBJS; i++)
	  RunObj[i] = NULL;

	number = 0;
}


void t_runtime_root::Add(t_runtime_object *o)
{
	if (number >= MAX_RUNTIME_OBJS)
	   return;	// don't exceed defined buffer

	if (o == NULL)
	   return;	// don't add wrong objects

	for (int i=0; i<number; i++)
	   if (RunObj[i] == o)
	      return;	// don't add already existing objects

	RunObj[number] = o;
	RunObj[number]->id = number;
	number++;
}



void t_runtime_root::Remove(t_runtime_object *o)
{
	if (number <= 0)
	   return;

	int i;
	for (i=0; i<number; i++)
	  {
           if (RunObj[i] == o)
	      break;
	  }

	if (i == number)
	   return;		// nothing to delete	

	number--;
	RunObj[i] = RunObj[number];	// swap last to this position
	RunObj[number] = NULL;
}

void t_runtime_root::Done(void)
{
//	fprintf(stderr,"t_runtime_root::Done()\n");
	stop();

	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] != NULL)
	      RunObj[i]->Done();
	  }	
}

void t_runtime_root::do_slider(FL_OBJECT *ob, long data)
{
	int prefix = data / 1000;
	int action = data % 1000;
	
	if (prefix >= number)
	   return;
	
	RunObj[prefix]->slider_cb(ob,action);
}


char **t_runtime_root::find_slidername(FL_OBJECT *ob,int &ron)
{
	for (int k=0; k < number; k++)
          {
//	   fprintf(stderr,"r_obj[%d].",k);
	   for (int n=0; n < RunObj[k]->nsliders; n++)	
	     {
//	      fprintf(stderr,"slider[%d].",n);

	      if (RunObj[k]->fd_demo->slider[n] == ob)
		 {
		  ron = k;
		  return &RunObj[k]->fd_demo->slider[n]->label; 
		 }
             }
//	   fprintf(stderr,"\n");
	  }

	return NULL;
}

void t_runtime_root::do_button(FL_OBJECT *ob, long data)
{
	int prefix = data / 1000;
	int action = data % 1000;

	if (prefix >= number)
	   return;

	RunObj[prefix]->button_cb(ob,action);
}

void t_runtime_root::do_choice(FL_OBJECT *ob, long data)
{
	int prefix = data / 1000;
	int action = data % 1000;

	if (prefix >= number)
	   return;

	RunObj[prefix]->choice_cb(ob,action);
}

int t_runtime_root::calculate(char allow_learn, char any_new)
{
	if (number < 1)
	   return 0;
	
	int k = 0;
	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] != NULL)
	      {
	       if (RunObj[i]->active_calc == false)
	   	  {
		   if (RunObj[i]->calculate_module(allow_learn, any_new) == true)
		      k++;	
		  } 
              }
          }

	state = _s_running;
	return k;
}

int t_runtime_root::control(void)
{
	if (number < 1)
	   return 0;
	
	int k = 0;
	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] != NULL)
	      {
	       if (RunObj[i]->active_ctl == false)
		  {
		   if (RunObj[i]->control_module() == true)
		       k++;
		  }
	      }
	  }	

	state = _s_running;
	return k;
}

char t_runtime_root::NeedCanvas(void)
{
	if (number < 1)
	   return false;
	
	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] == NULL)
	      continue;
	
	   if (RunObj[i]->visible == true)
	      return true;
	  }	

	return false;
}

void t_runtime_root::start(void)
{
#ifdef DEBUG
	fprintf(stderr,"t_runtime_root::start() number=%d\n",number);
#endif
	if (number < 1)
	   return;
	
	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] != NULL)
	      {
	       if ((RunObj[i]->active_ctl != true) || (RunObj[i]->active_calc != true))
		   RunObj[i]->start_module();
	      }	
	  }	

	state = _s_started;
}

void t_runtime_root::stop(void)
{
	if (number < 1)
	   return;
	
	for (int i=0; i<number; i++)
	  {
//	   fprintf(stderr,"root::stop(%d)\n",i);
	   if (RunObj[i] != NULL)
              RunObj[i]->stop_module();
	  }	

//	Robot_.MsgQueue.send_motorspeed(0,0);

	t_robot_basic *r = (t_robot_basic *)&Robot_;

	int L,R;
	L = 0; R = 0;
	r->SetMotorSpeed(L,R);
	state = _s_cleared;
}

void t_runtime_root::pause(char pp)
{
	if (number < 1)
	   return;
	
	for (int i=0; i<number; i++)
	  {
//	   fprintf(stderr,"root::stop(%d)\n",i);
	   if (RunObj[i] != NULL)
              RunObj[i]->pause_module(pp);
	  }	

//	Robot_.MsgQueue.send_motorspeed(0,0);
	if (pp == true)
	   {
	    t_robot_basic *r = (t_robot_basic *)&Robot_;
	    int L,R;
	    L = 0; R = 0;
	    r->SetMotorSpeed(L,R);

	    if (state == _s_running)
		state = _s_paused;

	    return;
	   } 

	if (state == _s_paused)
	   state = _s_running;
}


int t_runtime_root::Execute_Cntl(char Do, char pp)
{
//	fprintf(stderr,"t_runtime_root::Execute_Cntl() state=%d\n",state);
	int v = 0;

	switch (state)
	  {
	   case _s_halted:
//	     if (Do == true)
	     stop();
	     break;

	   case _s_cleared:
	   case _s_undefined:
	     if (Do == true)
 	         start();
	     break;

	   case _s_started:
	   case _s_running:
	     if ((pp == true) && (state == _s_running))	
		{
	         pause(pp);
		 return 0;
	        }
	      
	     if ((pp == false) && (state == _s_paused))
	        {
		 pause(pp);
	         return 0;
		}
 
             if (Do == true)
	         v += control();
              else
		 state = _s_halted;
             break;
	  }

	return v;
}


int t_runtime_root::Execute_Calc(char Do, char allow_learn, char any_new)
{
//	fprintf(stderr,"t_runtime_root::Execute_Calc() state=%d, this=0x%08lx\n",state,(long)this);
	int v = 0;

	switch (state)
	  {
	   case _s_halted:
	     if (Do == true)
		 stop();

	   case _s_cleared:
	   case _s_undefined:
	     if (Do == true)
		 start();
	     break;

	   case _s_started:
	   case _s_running:	
             if (Do == true)
		 v += calculate(allow_learn, any_new);
              else
		 state = _s_halted;
             break;
	  }

	return v;
}

void t_runtime_root::Show(void)
{
//	fprintf(stderr,"t_runtime_root::Show() state=%d, this=0x%08lx\n",state,(long)this);

	if (number < 1)
	   return;
	
	for (int i=0; i<number; i++)
	  {
	   if (RunObj[i] != NULL)
	      if (RunObj[i]->visible == true)
                 RunObj[i]->Show();
	  }	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


char dummyboxes[3][8] = {"erste\0","zweite\0","dritte\0"};
char dummysliders[2][16] = {"uno\0","dos\0"};
char dummychoices[2][64] = {"quattro@A|B\0","cinco@4|7|9\0"};

char log_sliders[2][16] = {"log.intvl.\0","log.avg.\0"};

void t_runtime_object::Init(char *name, RobotContext *robot)
{
	char *cb_pointers[] = {&dummyboxes[0][0],&dummyboxes[1][0],&dummyboxes[2][0]};
	char *sl_pointers[] = {&dummysliders[0][0],&dummysliders[1][0]};
	char *ch_pointers[] = {&dummychoices[0][0],&dummychoices[1][0]};
 
	Init(name,robot,0,2,3,2,sl_pointers,cb_pointers,ch_pointers);
}

void t_runtime_object::Init(char *name, RobotContext *robot,
				int ps,int nsld,int nbox,int nchc,
				char **sldn,char **boxn,char **chnam)
{
	if (name != NULL)
	    strcpy(Name,name);

#ifdef DEBUG
	fprintf(stderr,"t_runtime_object::Init(%s,0x%08lx,%d)\n",Name,(long)robot,robot->_FD_);
#endif
	cntl_age = 0;
	calc_age = 0;
	active_ctl = true;
	active_calc = true;
//	pre_state = 0;
//	paused = false;

	Robot = robot;
	state = _s_undefined;
	visible = false;
	props = ps;

	ncboxes = nbox;
	nchoices = nchc;

	if ((props & _p_logctl) > 0)
	    {
	     nsliders = nsld + 2;
	     SliderName = new char *[nsliders];
		
	     for (int i=0; i<2; i++)
		SliderName[i] = log_sliders[i];	

	     for (int i=2; i<nsliders; i++)
		SliderName[i] = sldn[i-2]; 
	    }
	  else
	    {
	     nsliders = nsld;
	     SliderName = sldn; 
	    } 

	BoxName = boxn;
	ChoiceName = chnam;

	fd_demo = create_form_demo(Name,id,nsliders,ncboxes,nchoices,
					SliderName,BoxName,ChoiceName);

	Logs.wival = 0;
	Logs.average = 1.0;
//	sl_prec = new int[nsliders];
//	for (int i=0; i<nsliders; i++)
//          sl_prec[i] = 2;
}


void t_runtime_object::Done(void)
{
#ifdef DEBUG
	fprintf(stderr,"t_runtime_object::Done(%s)\n",Name);
#endif
	stop_module();
	done_form_demo(fd_demo);
//	delete sl_prec;
}


char object_str[] = "native t_runtime_object::";

void t_runtime_object::Show(void)
{
	     fprintf(stderr,"%sShow() called but not overloaded\n",object_str);
}


typedef struct {
		 float top,bottom,value;	// might be float

		 int prec;

		 int no_se;
		 float step; 
		 float after; 
		} t_magic_xf_sl;

void t_runtime_object::Save(int fd)
{
	double low,high,value;
//	int on;

#ifdef DEBUG
	fprintf(stderr,"%sSave(%s)\n",object_str,name);
#endif
	char st[256];
	for (int i=0; i<nsliders; i++)
	  {
	   fl_get_slider_bounds(fd_demo->slider[i],&low,&high);
	   value = fl_get_slider_value(fd_demo->slider[i]);
	   
	   t_magic_xf_sl *S = (t_magic_xf_sl *)fd_demo->slider[i]->spec;

//	   char *C = (char *)fd_demo->slider[i]->spec;
//	   int *prec = (int *)&(C[12]);
//	   float *step = (float *)&(C[20]);
	// this is an ugly patch for a missing xforms routine
		//fl_get_slider_precision(fd_demo->slider[i]);

	   fprintf(stderr,"\n");

	   sprintf(st,"slider[%d].Label=\"%s\"\n",
		    i,fd_demo->slider[i]->label);
	   write(fd,st,strlen(st));
	   sprintf(st,"slider[%d].top=%g\n",i,high);
	   write(fd,st,strlen(st));
	   sprintf(st,"slider[%d].bottom=%g\n",i,low);
	   write(fd,st,strlen(st));
	   sprintf(st,"slider[%d].value=%g\n",i,value);
	   write(fd,st,strlen(st));


//	   fprintf(stderr,"sl[%d].i1=%g\t",i,S->top);
//	   fprintf(stderr,"sl[%d].i2=%g\t",i,S->bottom);
//	   fprintf(stderr,"sl[%d].i3=%g\t",i,S->value);
//	   fprintf(stderr,"sl[%d].prec=%d,%d\t",i,S->prec,*prec);
//	   fprintf(stderr,"sl[%d].step=%g,%g\t",i,S->step,*step);

#ifdef DEBUG
	   fprintf(stderr,"sl[%d].nose=%d\t",i,S->no_se);
	   fprintf(stderr,"sl[%d].after=%g\n",i,S->after);
#endif

	   sprintf(st,"slider[%d].prec=%d\n",i,S->prec);
	   write(fd,st,strlen(st));

	   sprintf(st,"slider[%d].step=%g\n",i,S->step);
	   write(fd,st,strlen(st));
	  }	

	for (int i=0; i<(ncboxes+_bt_num); i++)
	  {
	   sprintf(st,"box[%d].on=%d\n",i,fl_get_button(fd_demo->cbox[i]));
	   write(fd,st,strlen(st));
	  }	

	for (int i=0; i<(nchoices+_ch_num); i++)
	  {
	   int d = fl_get_choice(fd_demo->choice[i]);
	   sprintf(st,"choice[%d].selected=%d\n",i,d);
	   write(fd,st,strlen(st));

/*
	   for (int j=0; j<fl_get_choice_maxitems(fd_demo->choice[i]); i++)
	     {
	      sprintf(st,"choice[%d].select[%d]=%s\n",i,j,
			fl_get_choice_item_text(fd_demo->choice[i],j));
	      write(fd,st,strlen(st));
	     }
*/
	  }	
}

void t_runtime_object::Load(char *to_parse)
{
	double d,low,high;
	int i,j;

	if (sscanf(to_parse,"slider[%d].top=%lf",&i,&d) == 2)
	   {
	    if (i >= nsliders)
	       return;
	       
	    fl_get_slider_bounds(fd_demo->slider[i],&low,&high);
	    fl_set_slider_bounds(fd_demo->slider[i],low,d);
           }

	if (sscanf(to_parse,"slider[%d].bottom=%lf",&i,&d) == 2)
	   {
	    if (i >= nsliders)
	       return;
	       
	    fl_get_slider_bounds(fd_demo->slider[i],&low,&high);
	    fl_set_slider_bounds(fd_demo->slider[i],d,high);
           }
	    

	if (sscanf(to_parse,"slider[%d].value=%lf",&i,&d) == 2)
	   {
	    if (i >= nsliders)
	       return;
	       
	    fl_set_slider_value(fd_demo->slider[i],d);

	    switch (i)
	      {
	       case _sl_logival:
		 fprintf(stderr,"take log interval\n");
		 Logs.wival = (int)d;
		 break;	

	       case _sl_logavg:	
		 fprintf(stderr,"take log average\n");
	         Logs.average = d;
		 break; 
	      }
           }

	if (sscanf(to_parse,"slider[%d].prec=%d",&i,&j) == 2)
	   {
	    if (i >= nsliders)
	       return;
	       
	    fl_set_slider_precision(fd_demo->slider[i],j);
           }


	if (sscanf(to_parse,"slider[%d].step=%lf",&i,&d) == 2)
	   {
	    if (i >= nsliders)
	       return;
	       
	    fl_set_slider_step(fd_demo->slider[i],d);
           }

	if (sscanf(to_parse,"box[%d].on=%d",&i,&j) == 2)
	   {
	    if (i >= (ncboxes+_bt_num))
	       return;
	       
	    if (i != _bt_pause)
		fl_set_button(fd_demo->cbox[i],j);
           }

	if (sscanf(to_parse,"choice[%d].selected=%d",&i,&j) == 2)
	   {
	    if (i >= (nchoices+_ch_num))
	       return;
	       
	    fl_set_choice(fd_demo->choice[i],j);
           }
}

void t_runtime_object::button_cb(FL_OBJECT *ob,long data)
{
	switch(data)
	  {
	   case 0:	// load on start
	   case 1:	// save on stop
	     break;

	   case 2: 
	     Reset();    
	     break;

	   case 3: 
	     if (fl_get_button(ob))
		pause_module(true);
	      else
		pause_module(false);
	     break; 

	   default:
	     fprintf(stderr,"%sbutton_cb() not overloaded\n",object_str);
	     break; 	
	  }
}

void t_runtime_object::slider_cb(FL_OBJECT *ob,long data)
{
	switch(data)
	  {
	   case _sl_logival:
	     Logs.wival = (int)fl_get_slider_value(ob);
	     break;
	
	   case _sl_logavg:
	     Logs.average = fl_get_slider_value(ob);
	     break;

	   default:
	     fprintf(stderr,"%sslider_cb(%ld) not overloaded\n",object_str,data);
	     break; 	
	  }
}

void t_runtime_object::choice_cb(FL_OBJECT *ob,long data)
{
// 	switch(data)
// 	  {
// 	   default:
 	     fprintf(stderr,"%schoice_cb(%ld) not overloaded\n",object_str,data);
// 	     break; 	
// 	  }
}

void t_runtime_object::Reset(void)
{
	     fprintf(stderr,"%sReset() not overloaded\n",object_str);
}

void t_runtime_object::Interactions(char show)
{
	if ((props & _p_extras) == 0)
	   return;

	char st[256];
	sprintf(st,"View %s Parameters",Name);

	if (show == true)
	   fl_show_form(fd_demo->demo,FL_PLACE_MOUSE,FL_FULLBORDER,st);	
         else
	   fl_hide_form(fd_demo->demo);
}

void t_runtime_object::make_logdata(void)
{
}

void t_runtime_object::make_logstring(void)
{
	strcpy(Logs.data,"\0");
}


char t_runtime_object::calculate_module(char allow_learn, char any_new)
{
	switch(state)
           {
	    case _s_cleared:
	    case _s_undefined:
	    case _s_paused:
	    case _s_halted: 
	       return false;
           }

	if (any_new == false)
	   {
//	    fprintf(stderr,"w "); 	
	    return false;
	   } 

	if (allow_learn == false)
	   {
//	    fprintf(stderr,"s "); 	
	    return false;
	   } 
	   
//	fprintf(stderr,"d "); 	
	calc_age++;
//	fprintf(stderr,"t_runtime_object::calculate_module(%s) state=%d, age=%ld\n",Name,state,calc_age);

	if (Logs.wival > 0)
	   {	
	    make_logdata();

	    if ((Logs.step % Logs.wival) == 0)
	       {
	        make_logstring();
	        write(Logs.fd,Logs.data,strlen(Logs.data));
	       }

	    Logs.step++;
	   }		

	return true;
}

char t_runtime_object::control_module(void)
{
	switch(state)
           {
	    case _s_cleared:
	    case _s_undefined:
	    case _s_paused:
	    case _s_halted: 
	       return false;

	    case _s_started:
	      state = _s_running;
	      break;	
           }

	cntl_age++;
//	fprintf(stderr,"t_runtime_object::control_module(%s) state=%d, age=%ld\n",Name,state,cntl_age);
	return true;
}


int t_runtime_object::openfile(char *envvar, char *rcpath, char *type, char *Name, char load)
{
	char st[256],*home,*dpath;
	int fd,flags;

	if (load == true)
            flags = O_RDONLY;
	 else
	    flags = O_RDWR|O_CREAT|O_TRUNC;


	dpath = getenv(envvar);

	if (dpath == NULL)
	   {
	    home = getenv("HOME");
            sprintf(st,"%s/.kcontrol/%s/%s_%s.txt",home,rcpath,type,Name);
	   } 
	 else
	    sprintf(st,"%s/%s_%s.txt",dpath,type,Name);	

	fd = open(st,flags,0640);

        if (fd == -1)
	   {
	    sprintf(st,"%s/%s_%s.txt",rcpath,type,Name); 
	    fd = open(st,flags,0640);

            if (fd == -1)
	       {
		sprintf(st,"%s_%s.txt",type,Name); 
		fd = open(st,flags,0640);
               }
	   }

	return fd;
}


char t_runtime_object::start_module(void)
{
	switch(state)
           {
	    case _s_started:
	    case _s_running:
	    case _s_halted: 
	       return false;

	    default:
	       pause_module(false);
	       break;	
           }

#ifdef DEBUG
	fprintf(stderr,"t_runtime_object::start_module(%s) state=%d\n",Name,state);
#endif
	Logs.fd = openfile("DATA_PATH","data","log",Name,false);

	state = _s_started;
	
	if (fl_get_button(fd_demo->cbox[_bt_load]))
	   {
	    char st[256],msg[256];

	    int cfg_fd = openfile("CFG_PATH","config","cfg",Name,true);	

	    if (cfg_fd == -1)
	       {
	        sprintf(msg,"open %s",st);
	        perror(msg);
		return true;		// quiet error exit	
	       }
	       
	    char *buffer = new char[16384 + 16];

	    int rd = read(cfg_fd,buffer,16384);

	    if (rd <= 0)
	       {
	        sprintf(msg,"read %s",st);
	        perror(msg);

		delete buffer;
	        close(cfg_fd); 
		return true;		// quiet error exit	
	       }

	    close(cfg_fd);

	    buffer[rd] = '\0';
	    buffer[rd+1] = '\0';
	    buffer[rd+2] = '\0';
	   
	    char *sp,*stop;
	
	    sp = &buffer[0];
	    stop = strchr(sp,'\n');

	    while (stop	 != NULL)	// scan for text lines
	      {
	       *stop = '\0';
	        Load(sp);		// call the virtual parser method
	        sp = stop + 1;
	    	stop = strchr(sp,'\n');	// next
	      } 

	    delete buffer;	
	   }

	return true;
}

void t_runtime_object::pause_module(char pp)
{
	if ((pp == true) && (state == _s_running))
#ifndef DEBUG
	   state = _s_paused;	
#else
	   {
	    state = _s_paused;
	    fprintf(stderr,"%s_demo paused\n",Name);	
	   }	
	 else
	    fprintf(stderr,"%s_state = %d\n",Name,state);	
#endif

	if ((pp == false) && (state == _s_paused))
	   state = _s_running;

	if (fl_get_button(fd_demo->cbox[_bt_pause]))
	   {
	    if (state != _s_paused)
	       {
		fl_set_button(fd_demo->cbox[_bt_pause],0);	
//		fprintf(stderr,"will release button\n");
	       } 		
	   }	
	 else
	   {	 
	    if (state == _s_paused) 
	       {
		fl_set_button(fd_demo->cbox[_bt_pause],1);	
//	        fprintf(stderr,"will push button\n");
	       }	
	   } 	
}

char t_runtime_object::stop_module(void)
{

//	Robot->SetMotorSpeed(0.0,0.0);

//	fprintf(stderr,"t_runtime_object::stop_module(%s) state=%d\n",Name,state);

	switch(state)
           {
	    case _s_cleared:
	       return false;
           }

#ifdef DEBUG
	fprintf(stderr,"t_runtime_object::stop_module(%s) state=%d\n",Name,state);
#endif
	close(Logs.fd);
	Logs.fd = -1;

	int pre_state = state;
	state = _s_cleared;

	if (pre_state == _s_undefined)
	   return false; 

	if (fl_get_button(fd_demo->cbox[_bt_save]))
	   {
	    int cfg_fd = openfile("CFG_PATH","config","cfg",Name,false);	
	    if (cfg_fd != -1)
	       {
		Save(cfg_fd);
	        close(cfg_fd);
	       }	
	     else
	       {
		char msg[256];
		sprintf(msg,"save %s",Name);
		perror(msg);
	       }
	   } 

	return true;
}




















/**/
/**/
/**/
/**/
/**/
/**/
/**/
/**/
/*****  End Of File  ****************************/
