#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <iostream.h>

#define Uses_otstream
#define Uses_MsgBox
#define Uses_TApplication
#define Uses_TButton
#define Uses_TDeskTop
#define Uses_TEvent
#define Uses_TKeys
#define Uses_TRect
#define Uses_TStatusDef
#define Uses_TStatusItem
#define Uses_TStatusLine
#define Uses_TTerminal
#define Uses_TWindow

#include <tv\tv.h>

//==============================================================================
class TStreamView:
  public TWindow
{
  int fhSave, fhClone, fhTmp;
  char *fnTmp;

  otstream *os;

  TTerminal *wn;

  public:
    TStreamView( TRect &r, const char *title, int fh, ushort bufSize = 2048 );
    ~TStreamView();

    void update();
};

//==============================================================================
//==============================================================================
//==============================================================================
TStreamView::TStreamView( TRect &r, const char *title, int fh, ushort bufSize ):
  TWindow( r, title, wnNoNumber ),
  TWindowInit( TStreamView::initFrame )
{
  r = getExtent();

  r.grow( -1, -1 );

  wn = new TTerminal( r,
                      standardScrollBar( sbHorizontal | sbHandleKeyboard ),
                      standardScrollBar( sbVertical | sbHandleKeyboard ),
                      bufSize );

  insert( wn );

  os = new otstream( wn );  // this is the stream that will be written to

  fhClone = fh;              // clone this handle
  fhSave  = dup( fhClone );  // save original handle

  fnTmp = tmpnam( 0 );                                               // create scratch IO file
  fhTmp = open( fnTmp, O_RDWR|O_CREAT|O_BINARY, S_IREAD|S_IWRITE );  // create scratch file

  dup2( fhTmp, fhClone );  // attach original to scratch
}

//==============================================================================
TStreamView::~TStreamView() {
  dup2( fhSave, fhClone );  // re-attach orignal
  ::close( fhSave );        // close handle
  ::close( fhTmp );         // close scratch file
  ::remove( fnTmp );        // delete scratch file

  delete os;
}

//==============================================================================
void TStreamView::update() {
  if ( tell( fhTmp ) > 0L ) {  // see if file-pointer moved
    char ch;
    long nBytes = tell( fhTmp );  // count the number of bytes in buffer

    lseek( fhTmp, 0L, SEEK_SET );  // reset file-pointer

    for ( long n = 0; n < nBytes && ::read( fhTmp, &ch, 1 ) > 0; ++n )  // clean out buffer
      *os << ch;

    lseek( fhTmp, 0L, SEEK_SET );  // reset file-pointer again
  }
}

//==============================================================================
//==============================================================================
//==============================================================================
class App:
  public TApplication
{
  TStreamView *stdOut;

  int count;

  public:
    App();
    ~App() { destroy( stdOut ); }

    void handleEvent( TEvent &evt ) { TApplication::handleEvent( evt ); }
    void idle();
};

//==============================================================================
App::App():
  TProgInit( App::initStatusLine, App::initMenuBar, App::initDeskTop )
{
  stdOut = new TStreamView( TRect( 1, 1, 50, 15 ), "stdout", fileno( stdout ) );

  if ( stdOut )
    deskTop->insert( stdOut );

  count = 0;
}

//==============================================================================
void App::idle() {
  TProgram::idle();

  printf( "%d\n", ++count );  // write to stdout!

  stdOut->update();
}

//==============================================================================
void main() {
  App app;

  app.run();
}