// to compile ff-c++ pipe.cpp
//  warning do not compile under windows...
#include "ff++.hpp"
#include <cstdio>
#include <unistd.h>


#ifdef WIN32
#include <Windows.h>


long ffsleep(long  s) { Sleep(s*1000);return 0;}
long ffusleep(long  s) {  Sleep(s/1000);return 0;}
#else
long ffsleep(long  s) { return sleep(s);}
long ffusleep(long  s) { return usleep(s);}
#endif

#if __clang_major__ > 4

#include "pstream.h"

struct pstream {
    redi::basic_pstream<char>  * fb;
    //stdiofilebuf * fb;
    ostream *os;
    istream *is;
    pstream(redi::basic_pstream<char>  *ff,std::ios_base::openmode mode)
    : fb(ff) ,os(0),is(0)
    {  if(verbosity>10) cout << " mode " << mode << endl;
        basic_iostream<char> *   bs = fb;//->rdbuf();
        ffassert(bs);
        if(mode & ios_base::in )  {is = dynamic_cast<istream *>(bs);  assert(is);}//new istream(fb);
        if(mode & ios_base::out ) {os = dynamic_cast<ostream *>(bs);assert(os);} //new ostream(fb);
        if(verbosity>10)  cout << is << " " << os << " ******* " << endl;
    }
    ~pstream() {
       // if(f)  delete f;;
     //   if(os) delete os;
      //  if(is) delete is;
        if(fb) delete(fb);
       // f=0;
        os=0;
        is=0;
        fb=0;
    }
    void flush()
    {
        if( os ) os->flush();
        //if( is ) is->flush();
    }
    
};

static pstream **  pstream_init(pstream **const & p,string * const & a,string * const & b)
{
    string mode= b ? *b: "w";
    if (mode.length() ==0) mode = "wr";
   /* redi::pstreams::pmode omp = redi::pstreams::pstdin|redi::pstreams::pstdout;
    if (mode == "r+" )      omp = redi::pstreams::pstdin|redi::pstreams::pstdout;
    else if( mode == "w"  ) omp = redi::pstreams::pstdout;
    else if(  mode == "r" ) omp = redi::pstreams::pstdin;
*/
    std::ios_base::openmode  om = ios_base::in| ios_base::out;
    if (mode == "r+" )      om =ios_base::in| ios_base::out;
    else if( mode == "w"  ) om = ios_base::out;
    else if(  mode == "r" ) om =  ios_base::in;
    else  ExecError("Invalide mode pstream r,r+,w ");
    if(verbosity>10)  *ffapi::cout()  << "pstream_init: om " << om << "(" <<ios_base::in << ios_base::out << ") mode:"
        << mode << " '" << *a <<"'"<<  endl;

    redi::basic_pstream<char>  *pp = new redi::pstream(a->c_str(),om);
    ffassert(pp);
    *p =  new pstream(pp,om);
    
    if ( !*p || !pp) {
        cerr << " Error openning pipe  " << *a << endl;
        ExecError("Error openning pipe");}
    
    return p;
};

static pstream **  pstream_init(pstream **const & p,string * const & a)
{
    return pstream_init(p,a,0);
};

#else
// VERSION GNU
#include <ext/stdio_filebuf.h>


typedef __gnu_cxx::stdio_filebuf<char> stdiofilebuf;
struct pstream {
    FILE * f; 
    stdiofilebuf * fb;
    ostream *os;
    istream *is;
    pstream(FILE *ff,std::ios_base::openmode mode)
     : f(ff),fb(new stdiofilebuf(f,mode) ) ,os(0),is(0)
    {  if(verbosity>10) cout << " mode " << mode << endl;
        if(mode & ios_base::in )  is = new istream(fb);
        if(mode & ios_base::out ) os = new ostream(fb);
        if(verbosity>10)  cout << is << " " << os << " ******* " << endl;
    }
    ~pstream() {
        if(f) pclose(f);
        if(os) delete os;
        if(is) delete is;
        if(fb) delete(fb);
        f=0;
        os=0;
        is=0;
        fb=0;
}
   void flush()
    {
        if( os ) os->flush();
        if( f) fflush(f);
    }
};
//typedef redi::pstream pstream;
// typedef std::string string;

static pstream **  pstream_init(pstream **const & p,string * const & a,string * const & b)
{
    string mode= b ? *b: "w";
    if (mode.length() ==0) mode = "wr";
    std::ios_base::openmode  om = ios_base::in| ios_base::out;
    if (mode == "r+" )      om =ios_base::in| ios_base::out;
    else if( mode == "w"  ) om = ios_base::out;
    else if(  mode == "r" ) om =  ios_base::in;
    else  ExecError("Invalide mode pstream r,r+,w ");
    if(verbosity>10)  *ffapi::cout()  << "pstream_init: om " << om << "(" <<ios_base::in << ios_base::out << ") mode:"
        << mode << " '" << *a <<"'"<<  endl;
#ifdef WIN32
    FILE * pp =_popen(a->c_str(),mode.c_str());
#else
    FILE * pp =popen(a->c_str(),mode.c_str());
#endif
    *p =  new pstream(pp,om);
    
    if ( !*p || !pp) {
        cerr << " Error openning pipe  " << *a << endl;
        ExecError("Error openning pipe");}
    
    return p;
};

static pstream **  pstream_init(pstream **const & p,string * const & a)
{
    return pstream_init(p,a,0);
};
#endif



AnyType pstream2o(Stack,const AnyType &a) {
    pstream* p = *PGetAny<pstream*>(a);
    ffassert(p->os);
    return   SetAny<ostream*>(p->os);}
    
AnyType pstream2i(Stack,const AnyType &a) {
        pstream* p = *PGetAny<pstream*>(a);
        ffassert(p->is);
        return   SetAny<istream*>(p->is);}

class istream_good { public:
    istream_good(istream * ff) :f(ff) {}
    istream * f;
    operator bool () const {return f->good();}
};
long cflush(pstream ** ppf) 
{
  pstream & f = **ppf;
    f.flush();
 // if( f.os ) f.os->flush();
 // if( f.f) fflush(f.f);
  return  0; 
}; 
inline istream_good to_istream_good(pstream **f){ ffassert((**f).is) ; return istream_good((**f).is);}
inline bool get_eof(pstream ** p){ return (**p).is ? (**p).is->eof(): EOF;}
void inittt()
{
  Dcl_TypeandPtr<pstream*>(0,0,::InitializePtr<pstream*>,::DeletePtr<pstream*>);
  atype<istream* >()->AddCast( new E_F1_funcT<istream*,pstream**>(pstream2i));
  atype<ostream* >()->AddCast( new E_F1_funcT<ostream*,pstream**>(pstream2o));
  TheOperators->Add("<-",new OneOperator2_<pstream**,pstream**,string*>(pstream_init) );
  TheOperators->Add("<-",new OneOperator3_<pstream**,pstream**,string*,string*>(pstream_init) );
  zzzfff->Add("pstream",atype< pstream ** >());
  Add<pstream**>("good",".",new OneOperator1<istream_good,pstream**>(to_istream_good));
  Add<pstream**>("eof",".",new OneOperator1<bool,pstream**>(get_eof));
  Global.Add("flush","(",new OneOperator1<long,pstream **> ( cflush)) ; 
  Global.Add("sleep","(",new OneOperator1<long,long> ( ffsleep)) ; 
  Global.Add("usleep","(",new OneOperator1<long,long> ( ffusleep)) ; 
#ifdef WIN32
   Global.New("onWIN32",CConstant<bool>(true));
#else    
  Global.New("onWIN32",CConstant<bool>(false));
#endif
}

LOADFUNC(inittt);
