// Copyright (C) 2001 Jean-Marc Valin

#include "Node.h"

using namespace std;

namespace FD {

class Feedback;

DECLARE_NODE(Feedback)
/*Node
 *
 * @name Feedback
 * @category Flow
 * @description Feedback objects with a delay of n iterations.
 *
 * @input_name INPUT
 * @input_description The input object
 *
 * @input_name BEFORE
 * @input_description When count < delay, pull the input from here
 *
 * @output_name OUTPUT
 * @output_description The output object = input object
 *
 * @output_name DELAY
 * @output_description The delayed output of DELAY iteration
 *
 * @parameter_name DELAY
 * @parameter_description Number of iteration for the delay
 * @parameter_type int
 *
 * @parameter_name BEFORE_LIMIT
 * @parameter_description When count - DELAY is smaller or equal to BEFORE_LIMIT, the input is pulled from BEFORE at (DELAY - count + BEFORE_LIMIT)
 * @parameter_type int
 *
END*/


class Feedback : public Node {
protected:
   int inputID;
   int beforeID;
   int delayID;
   int outputID;

   bool insideRequest;

   int delay;
   int beforeLimit;

   int delayRecurs;

public:
   Feedback(string nodeName, ParameterSet params)
      : Node(nodeName, params)
      , insideRequest(false)
   {
      try {
         inputID = addInput("INPUT");
         beforeID = addInput("BEFORE");
	 outputID=addOutput("OUTPUT");
	 delayID = addOutput("DELAY");
	 
	 delay = dereference_cast<int> (parameters.get ("DELAY"));
	 if (delay < 1)
	    throw new NodeException(NULL, "DELAY <= 0 would cause an infinite recursion", __FILE__, __LINE__);

	 if (parameters.exist("BEFORE_LIMIT"))
	    beforeLimit = dereference_cast<int> (parameters.get ("BEFORE_LIMIT"));
	 else
	    beforeLimit = 0;


      } catch (BaseException *e)
      {
         throw e->add(new NodeException (NULL, "Exception caught in Feedback constructor", __FILE__, __LINE__));
      }
      delayRecurs=-1;
   }

   void initialize()
   {
      Node::initialize();
      ParameterSet req;
      req.add("LOOKBACK", ObjectRef(Int::alloc(delay)));
      req.add("INORDER", nilObject);
      inputs[inputID].node->request(inputs[inputID].outputID,req);

      ParameterSet req2;
      req2.add("LOOKBACK", ObjectRef(Int::alloc(beforeLimit+delay)));
      inputs[beforeID].node->request(inputs[beforeID].outputID,req2);

   }


   /**Modified the request passing method in order to avoid strange behaviours*/
   virtual void request(int output_id, const ParameterSet &req) 
   {
      if (output_id == outputID)
	 inputs[inputID].node->request(inputs[inputID].outputID,req);

   }
      
   ObjectRef getOutput(int output_id, int count)
   {
      if (output_id == outputID)
	 return getInput(inputID, count);
      else if (output_id == delayID)
      {
	 //FIXME: Should check for infinite loop if BEFORE is connected to DELAY?
	 if (count-delay-beforeLimit < 0)
	    return getInput(beforeID, beforeLimit+delay-count);
	 //cerr << delayRecurs << endl;
	 if (delayRecurs != -1 && count-delay >= delayRecurs)
	    throw new NodeException (this, "Infinite loop detected, breaking out", __FILE__, __LINE__);
	 if (count-delay > delayRecurs)
	    delayRecurs=count-delay;
	 ObjectRef ret = getInput(inputID, count-delay);
	 delayRecurs=-1;
	 return ret;
      } else {
	 throw new NodeException (this, "Output not found", __FILE__, __LINE__);
      }
   }
      
};

}//namespace FD


syntax highlighted by Code2HTML, v. 0.9.1