/*
 *  slibuiltins.cc
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST 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 of the License, or
 *  (at your option) any later version.
 *
 *  NEST 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 NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/* 
    Interpreter builtins
*/
#include "slibuiltins.h"
#include "interpret.h"
#include "callbackdatum.h"
#include "arraydatum.h"
#include "integerdatum.h"
#include "stringdatum.h"
#include "iteratordatum.h"
#include "functiondatum.h"

void IlookupFunction::execute(SLIInterpreter *i) const
{
    i->EStack.pop(2);
}

void IsetcallbackFunction::execute(SLIInterpreter *i) const
{
       // move the hopefully present callback action
        // into the interpreters callback token.
    i->EStack.pop();
    assert(dynamic_cast<CallbackDatum *>(i->EStack.top().datum()) !=NULL);
    i->EStack.pop_move(i->ct);
}

void IiterateFunction::backtrace(SLIInterpreter *i, int p) const
{
  ProcedureDatum const *pd= dynamic_cast<ProcedureDatum *>(i->EStack.pick(p+2).datum());   
  assert(pd != NULL);

  IntegerDatum   *id= dynamic_cast<IntegerDatum *>(i->EStack.pick(p+1).datum());
  assert(id !=NULL);

  std::cerr << "In procedure:" << std::endl;
  
  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr << std::endl;
}

void IiterateFunction::execute(SLIInterpreter *i) const
{
/* 
   This function is responsible for executing a procedure
   object. Iiterate expects the procedure to execute as first
   and the iteration counter as second argument.

   Like in all internal function, no error checking is done.
   
   */

/* Stack Layout:
      3       2       1
   <proc>  <pos>   %iterate
*/

    ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());   
    long &pos=static_cast<IntegerDatum *>(i->EStack.pick(1).datum())->get();

   while( pd->index_is_valid(pos))
   {
       const Token &t=pd->get(pos);
       ++pos;
  
       i->code_executed++;   // code coverage 

       if( t->is_executable())
       {
	   i->EStack.push(t);
	   return;
       }
       i->OStack.push(t);
   }
   
   i->EStack.pop(3);
   i->dec_call_depth();
}

void IloopFunction::execute(SLIInterpreter *i) const
{
        // stack: mark procedure n   %loop
        // level:  4      3      2     1
    
    ProcedureDatum
        const *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum()); 
    long &pos=static_cast<IntegerDatum *>(i->EStack.pick(1).datum())->get();

    while( proc->index_is_valid(pos))
    {
	const Token &t(proc->get(pos));
	++pos;
	if( t->is_executable())
	{
	    i->EStack.push(t);
	    return;
	}

	i->OStack.push(t);
    }
   
    pos =0;
}

void IloopFunction::backtrace(SLIInterpreter *i, int p) const
{
  ProcedureDatum const *pd= dynamic_cast<ProcedureDatum *>(i->EStack.pick(p+2).datum());   
  assert(pd !=NULL);

  IntegerDatum   *id= dynamic_cast<IntegerDatum *>(i->EStack.pick(p+1).datum());
  assert(id != NULL);

  std::cerr << "During loop:" << std::endl;
  
  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}


/**********************************************/
/* %repeat                                    */
/*  call: mark  count proc  n %repeat         */
/*  pick   5      4     3   2    1            */        
/**********************************************/
void IrepeatFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum
         *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());
    
   long &pos=static_cast<IntegerDatum *>(i->EStack.pick(1).datum())->get();
   while( proc->index_is_valid(pos))
   {
       const Token &t=proc->get(pos);
       ++pos;
       if( t->is_executable())
       {
	   i->EStack.push(t);
	   return;
       }
       i->OStack.push(t);
   }
   
   long &lc=static_cast<IntegerDatum *>(i->EStack.pick(3).datum())->get();
   if( lc > 0 )
   {
       pos=0;     // reset procedure iterator
       --lc;
   }
   else
   {
       i->EStack.pop(5);
       i->dec_call_depth();
    }
}

void IrepeatFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(count != NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+2).datum());   
  assert(pd!= NULL);

  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+1).datum());
  assert(id != NULL);

  std::cerr << "During repeat with " << count->get() << " iterations remaining." << std::endl;
  
  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

/*****************************************************/
/* %for                                              */
/*  call: mark incr limit count proc  n  %for        */
/*  pick   6     5    4     3    2    1    0         */        
/*****************************************************/
void IforFunction::execute(SLIInterpreter *i) const
{
    
    IntegerDatum *proccount=
        static_cast<IntegerDatum *>(i->EStack.pick(1).datum());
    
    ProcedureDatum
        const *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());
    
    long &pos=proccount->get();

    while( proc->index_is_valid(pos))
    {
	const Token &t= proc->get(pos);
	++pos;
	if( t->is_executable())
	{
	    i->EStack.push(t);
	    return;
	}
	i->OStack.push(t);
      }
        
    IntegerDatum *count=
	static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    
    IntegerDatum *lim  =
	static_cast<IntegerDatum *>(i->EStack.pick(4).datum());
    
    IntegerDatum *inc  =
	static_cast<IntegerDatum *>(i->EStack.pick(5).datum());
    
    
    if(( (inc->get()> 0) && (count->get() <= lim->get())) ||
       ( (inc->get()< 0) && (count->get() >= lim->get())))
    {
	pos=0; // reset procedure interator
        
	i->OStack.push(i->EStack.pick(3)); // push counter to user
	(count->get()) += (inc->get());    // increment loop counter
    }
    else
    {
	i->EStack.pop(7);
	i->dec_call_depth();
    }
}

void IforFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(count!=NULL);
  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+2).datum());   
  assert(pd != NULL);
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+1).datum());
  assert(id != NULL);

  std::cerr << "During for at iterator value " << count->get() << "." << std::endl;
  
  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

/*********************************************************/
/* %forallarray                                          */
/*  call: mark object count proc n %forallarray      */
/*  pick    5     4    3     2    1    0               */        
/*********************************************************/
void IforallarrayFunction::execute(SLIInterpreter *i) const
{
    
    IntegerDatum *proccount=
        static_cast<IntegerDatum *>(i->EStack.pick(1).datum());
    
    ProcedureDatum
        const *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());
 
    long &pos=proccount->get();

    while( proc->index_is_valid(pos))
    {
	const Token &t= proc->get(pos);
	++pos;
	if( t->is_executable())
	{
	    i->EStack.push(t);
	    return;
	}
	i->OStack.push(t);
    }
 
   IntegerDatum *count=
	static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
     ArrayDatum *ad  =
	static_cast<ArrayDatum *>(i->EStack.pick(4).datum());
        
    long &idx=count->get();
    
    if(ad->index_is_valid(idx))
    {
	pos=0; // reset procedure interator
        
	i->OStack.push(ad->get(idx)); // push counter to user
	++idx;
    }
    else
    {
	i->EStack.pop(6);
	i->dec_call_depth();
    }
}


void IforallarrayFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(count!=NULL);

  std::cerr << "During forall (array) at iteration " << count->get() << "." << std::endl;

}


/*********************************************************/
/* %foralliter                                          */
/*  call: mark iterator proc %foralliter                */
/*  pick   3     2    1      0                          */        
/*********************************************************/
void IforalliterFunction::execute(SLIInterpreter *i) const
{
  IteratorDatum *iter=
    static_cast<IteratorDatum *>(i->EStack.pick(2).datum());

    
   if(iter->pos() < iter->end())
    {

      i->OStack.push(iter->pos());  // push current element to operand stack
      iter->incr();
      i->EStack.push(i->EStack.pick(1));
      if(i->step_mode())
      {
	std::cerr << "foralliter:"
		  << " Limit: " << iter->end()
		  << " Pos: "   << iter->pos() ;
	i->OStack.pick(0).pprint(std::cerr);
	std::cerr << std::endl;
      }
    }
    else
    {
      i->EStack.pop(4);
      i->dec_call_depth();
    }
}

void IforalliterFunction::backtrace(SLIInterpreter *i, int p) const
{
  IteratorDatum   *iter= static_cast<IteratorDatum *>(i->EStack.pick(p+2).datum());

  std::cerr << "During forall (iterator) at iteration " << iter->pos() << "." << std::endl;

}



/*********************************************************/
/* %forallindexedarray                                   */
/*  call: mark object limit count proc forallindexedarray  */
/*  pick   5      4    3     2    1      0         */        
/*********************************************************/
void IforallindexedarrayFunction::execute(SLIInterpreter *i) const
{
    IntegerDatum *count=
        static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    IntegerDatum *limit=
        static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
     
    long &cnt=count->get();
    if( cnt< limit->get())
    {
      ArrayDatum *obj=
	static_cast<ArrayDatum *>(i->EStack.pick(4).datum());

      i->OStack.push(obj->get(cnt));  // push element to user
      i->OStack.push_by_pointer(new IntegerDatum(cnt));            // push index to user
      ++cnt;
      i->EStack.push(i->EStack.pick(1));
      // if(i->step_mode())
      // {
      // 	std::cerr << "forallindexed:"
      // 		  << " Limit: " << limit->get()
      // 		  << " Pos: " << count->get() -1
      // 		  << " Iterator: "; 
      // 	i->OStack.pick(1).pprint(std::cerr);
      // 	std::cerr << std::endl;
      // }
    }
    else
    {
      i->EStack.pop(6);
      i->dec_call_depth();
    }

}

void IforallindexedarrayFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count!=NULL);

  std::cerr << "During forallindexed (array) at iteration " << count->get()-1 << "." << std::endl;

}

void IforallindexedstringFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count!=NULL);

  std::cerr << "During forallindexed (string) at iteration " << count->get()-1 << "." << std::endl;

}

/*********************************************************/
/* %forallindexedarray                                   */
/*  call: mark object limit count proc forallindexedarray  */
/*  pick   5      4    3     2    1      0         */        
/*********************************************************/
void IforallindexedstringFunction::execute(SLIInterpreter *i) const
{
    IntegerDatum *count=
        static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    IntegerDatum *limit=
        static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    
    if(count->get() < limit->get())
    {
      StringDatum const *obj=
	static_cast<StringDatum *>(i->EStack.pick(4).datum());

      i->OStack.push((*obj)[count->get()]);  // push element to user
      i->OStack.push(count->get());          // push index to user
      ++(count->get());
      i->EStack.push(i->EStack.pick(1));
      if(i->step_mode())
      {
	std::cerr << "forallindexed:"
		  << " Limit: " << limit->get()
		  << " Pos: " << count->get()
		  << " Iterator: "; 
	i->OStack.pick(1).pprint(std::cerr);
	std::cerr << std::endl;
      }
    }
    else
    {
      i->EStack.pop(6);	    
      i->dec_call_depth();
    }

}

void IforallstringFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count!=NULL);

  std::cerr << "During forall (string) at iteration " << count->get()-1 << "." << std::endl;

}
/*********************************************************/
/* %forallstring                                         */
/*  call: mark object limit count proc %forallarray  */
/*  pick   5      4    3     2    1      0         */        
/*********************************************************/
void IforallstringFunction::execute(SLIInterpreter *i) const
{
    IntegerDatum *count=
        static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    IntegerDatum *limit=
        static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    
    if(count->get() < limit->get())
    {
      StringDatum const *obj=
	static_cast<StringDatum *>(i->EStack.pick(4).datum());
      i->OStack.push_by_pointer(new IntegerDatum((*obj)[count->get()]));  // push element to user
      ++(count->get());
      i->EStack.push(i->EStack.pick(1));
      if(i->step_mode())
      {
	std::cerr << "forall:"
		  << " Limit: " << limit->get()
		  << " Pos: " << count->get()
		  << " Iterator: "; 
	i->OStack.top().pprint(std::cerr);
	std::cerr << std::endl;
      }
    }
    else
    {
      i->EStack.pop(6);
      i->dec_call_depth();
    }

}