// Copyright (C) 1999 Jean-Marc Valin

#include "BufferedNode.h"
#include "net_types.h"
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "Stream.h"

#ifdef WIN32

#include <windows.h>
#define sleep(x) Sleep((x)*1000)

#endif

using namespace std;

namespace FD {

class InputStream;

DECLARE_NODE(InputStream)
/*Node
 *
 * @name InputStream
 * @category IO
 * @description Creates a read-only stream from a filename
 *
 * @input_name INPUT
 * @input_description The file name
 * @input_type string
 *
 * @output_name OUTPUT
 * @output_description The Stream
 * @output_type Stream
 *
 * @parameter_name TYPE
 * @parameter_type String
 * @parameter_description Type of stream: stream, fd, or FILE (default stream)
 *
 * @parameter_name RETRY
 * @parameter_type int
 * @parameter_description If set to N, InputStream will retry N times on open fail
 *
END*/

class InputStream : public BufferedNode {

protected:
   
   /**The ID of the 'output' output*/
   int outputID;

   /**The ID of the 'input' input*/
   int inputID;

   typedef enum {fd, fptr, cpp} StreamType;

   StreamType type;

   int retry;

public:
   InputStream(string nodeName, ParameterSet params) 
      : BufferedNode(nodeName, params)
   {
      outputID = addOutput("OUTPUT");
      inputID = addInput("INPUT");
      if (parameters.exist("TYPE"))
      {
	 const String &strType = object_cast<String> (parameters.get("TYPE"));
	 if (strType == "stream")
	    type = cpp;
	 else if (strType == "FILE")
	    type = fptr;
#ifndef WIN32
	 else if (strType == "fd")
	    type = fd;
#endif
	 else 
	    throw new NodeException(NULL, "Bad stream type: " + strType, __FILE__, __LINE__);
      }
      else
	 type = cpp;
      
      if (parameters.exist("RETRY"))
	 retry = dereference_cast<int> (parameters.get("RETRY"));
      else
	 retry = 0;
   }

   void calculate(int output_id, int count, Buffer &out)
   {
      //cerr << "opening for count = " << count << endl;
      ObjectRef inputValue = getInput(inputID, count);
      const String &fileName = object_cast<String> (inputValue);

      ObjectRef openedFile;
      switch (type)
      {
	 case cpp:
	 {
	    ifstream *file = new ifstream(fileName.c_str());
	    if (retry)
	    {
	       int retryLeft=retry;
	       int retryTime=5;
	       while (file->fail() && retryLeft--)
	       {
		  cerr << "deleting\n";
		  delete file;
		  sleep (retryTime);
		  retryTime *= 2;
		  file = new ifstream(fileName.c_str());
		  cerr << "retrying\n";
		  cerr << file->fail() << " " << retryLeft << endl;
	       }
	    }
	    if (file->fail())
	    {
	       delete file;
	       throw new NodeException(this, "InputStream: cannot open file: " + fileName, __FILE__, __LINE__);
	    }
	    openedFile = ObjectRef (new IStream(file));
	 }
	    break;
	 case fptr:
	 {
	    FILE *file = fopen (fileName.c_str(), "r");
	    if (!file)
	       throw new NodeException(this, "InputStream: cannot open file: " + fileName, __FILE__, __LINE__);
	    openedFile = ObjectRef (new FILEPTR(file));
	 }
	    break;
#ifndef WIN32
	 case fd:
	 {
	    int file = open (fileName.c_str(), O_RDONLY);
	    if (file == -1)
	       throw new NodeException(this, "InputStream: cannot open file: " + fileName, __FILE__, __LINE__);
	    openedFile = ObjectRef (new FILEDES(file));
	 }
	    break;
#endif
      }
      out[count] = openedFile;
   }

protected:
   /**Default constructor, should not be used*/
   InputStream() {throw new GeneralException("InputStream copy constructor should not be called",__FILE__,__LINE__);}

NO_ORDER_NODE_SPEEDUP(InputStream)
};




}//namespace FD


syntax highlighted by Code2HTML, v. 0.9.1