// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1996
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        tifdirect.C
// 
// Purpose:     
// 
// Created:     5 Jan 1997   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// $Id: tifdirect.C,v 1.4 1997/03/03 19:48:09 jfasch Exp $
// 
// $Log: tifdirect.C,v $
// Revision 1.4  1997/03/03 19:48:09  jfasch
// just a comment
//
// Revision 1.3  1997/03/03 09:54:56  jfasch
// interpret a timeout in request (only in the blocking version)
//
// Revision 1.2  1997/02/13 12:16:10  jfasch
// no need to call INETSocket::thatAddress() (which does getpeername()
// (which does not work (at least not straight after a nonblocking
// connect()) under FUCKING NT)) because I do know where I am connected
// to.
//
// Revision 1.1  1997/02/04 13:48:16  jfasch
// Initial revision
//
// 
// </file> 
#include "tifdirect.h"

#include "tsockio.h"

#include <hyperg/utils/assert.h>
#include <hyperg/utils/fields.h>
#include <hyperg/utils/new.h>


// --------------------------------------------------------------------
class TIFDirectPair {
public:
   TIFDirectPair (long refno /*of the Connecter request*/, const TIOINETRequestPtr&) ;
   long refno() const { return refno_; }
   const TIOINETRequestPtr& request() const { return request_; }

   bool operator == (const TIFDirectPair& p) const { return refno_==p.refno_; }
   bool operator != (const TIFDirectPair& p) const { return refno_!=p.refno_; }
   bool operator <  (const TIFDirectPair& p) const { return refno_< p.refno_; }

private:
   long refno_ ;
   TIOINETRequestPtr request_ ;
public:
   // never used directly. just to be able to instantiate (and to be
   // used inside) the "template" below.
   TIFDirectPair() : refno_(-1) {}
//    TIFDirectPair& operator = (const TIFDirectPair&) ;
} ;
inline TIFDirectPair :: TIFDirectPair (long refno, const TIOINETRequestPtr& req) 
: refno_(refno),
  request_(req) {}

Fieldsdeclare (TIFDirectLookupBase, TIFDirectPair) ;
Fieldsimplement (TIFDirectLookupBase, TIFDirectPair) ;

class TIFDirectLookup : public TIFDirectLookupBase {
public:
   bool insert (long refno, const TIOINETRequestPtr&) ;
   bool removeByRefno (long refno, TIOINETRequestPtr&) ;
   bool removeByRequest (const TIOINETRequest*, long& refno) ;
} ;
inline bool TIFDirectLookup :: insert (long ref, const TIOINETRequestPtr& req) {
   append (TIFDirectPair (ref, req)) ;
   return true ;
}
bool TIFDirectLookup :: removeByRefno (long ref, TIOINETRequestPtr& req) {
   for (int i=0 ; i<TIFDirectLookupBase::count() ; i++) {
      const TIFDirectPair& p = TIFDirectLookupBase::operator[] (i) ;
      if  (p.refno() == ref) {
         req = p.request() ;
         TIFDirectLookupBase::remove (i) ;
         return true ;
      }
   }
   return false ;
}
bool TIFDirectLookup :: removeByRequest (const TIOINETRequest* req, long& ref) {
   for (int i=0 ; i<TIFDirectLookupBase::count() ; i++) {
      const TIFDirectPair& p = TIFDirectLookupBase::operator[] (i) ;
      if  (p.request().ptr() == req) {
         ref = p.refno() ;
         TIFDirectLookupBase::remove (i) ;
         return true ;
      }
   }
   return false ;
}

// --------------------------------------------------------------------
Verbose TIOINETFactoryDirect :: verbose ;
const char* TIOINETFactoryDirect :: version1 = "TIOINETFactoryDirect: $Id: tifdirect.C,v 1.4 1997/03/03 19:48:09 jfasch Exp $" ;

TIOINETFactoryDirect :: TIOINETFactoryDirect() 
: ref_req_(HGNEW (TIFDirectLookup)) {}

TIOINETFactoryDirect :: ~TIOINETFactoryDirect() {
   hgassert (!ref_req_->count(), "TIOINETFactoryDirect::~TIOINETFactoryDirect(): "
             "still got pending requests") ;
   HGDELETE (ref_req_) ;
}

TIOINETResponsePtr TIOINETFactoryDirect :: connect (const TIOINETRequestPtr& req) {
   hgassert (req, "TIOINETFactoryDirect::connect(): nil request") ;
   DEBUGNL ("TIOINETFactoryDirect::connect(): address "<<req.ptr()->address().host()<<
            ", port "<<req.ptr()->address().port()<<", user "<<req.ptr()->user()) ;

   // set the request\'s worker to be me.
   TIOINETFactory::set_worker_(req) ;

   // and do it 
   if (req.ptr()->user())
      return make_nonblocking_(req) ;
   else 
      return make_blocking_(req) ;
}

TIOINETResponsePtr TIOINETFactoryDirect :: make_blocking_(const TIOINETRequestPtr& req) {
   INETSocketPtr conn  (HGNEW (
      INETSocket (req.ptr()->address(),
                  (req.ptr()->timeout()>0) ? req.ptr()->timeout() : 0))) ;
   TIOINETResponsePtr res ;
   if (conn.ptr()->ok()) {
      const INETAddress* thisaddr = conn.ptr()->thisAddress() ;
      if (! thisaddr) {
         DEBUGNL ("TIOINETFactoryDirect::make_blocking_(): "
                  "could not get this endpoint\'s address (getsockname() failed?)") ;
         res = TIOINETResponsePtr (HGNEW (TIOINETResponse (req.ptr(), 
                                                           TIOINETFactory::TIFNOADDR))) ;
      }
      else {
         TIOINETAttributes a (*thisaddr, req.ptr()->address()) ;
         res = 
               TIOINETResponsePtr (
                  HGNEW (TIOINETResponse (
                     req.ptr(), // the reference number for the issuer.
                     TransparentIOPtr (HGNEW (
                        TransparentSocketIO (conn.ptr()))), a))) ;
      }
   }
   else {
      DEBUGNL ("TIOINETFactoryDirect::make_blocking_(): connection failed") ;
      res = TIOINETResponsePtr (HGNEW (TIOINETResponse (req.ptr(), TIOINETFactory::TIFNOCONN))) ;
   }
   
   return res ;
}

TIOINETResponsePtr TIOINETFactoryDirect :: make_nonblocking_(const TIOINETRequestPtr& req) {
   ConnectArgs args ;
   args.address(req.ptr()->address()).refno(req.ptr()->id() /*the ids are unique, so that suffices*/) ;
   TIOINETResponsePtr res ;
   if (Connecter::instance().beginRequest (this, args)) {
      // keep it in my map for later lookup. Connecter will cll me
      // when the connection is ready.
      bool rv = ref_req_->insert (args.refno(), req) ;
      hgassert (rv, "TIOINETFactoryDirect::make_nonblocking_(): could not insert into map") ;
   }
   else {
      DEBUGNL ("TIOINETFactoryDirect::make_nonblocking_(): request failed *immediately*") ;
      res = TIOINETResponsePtr (HGNEW (TIOINETResponse (req.ptr(), TIOINETFactory::TIFNOCONN))) ;
   }

   return res ;
}

void TIOINETFactoryDirect :: connected (const ConnectArgs& args, const INETSocketPtr& conn) {
   DEBUGNL ("TIOINETFactoryDirect::connected() ("<<args.address().host()<<
            ", "<<args.address().port()<<')') ;
   hgassert (conn, "TIOINETFactoryDirect::connected(): nil socket") ;

   TIOINETRequestPtr req ;
   bool rv = ref_req_->removeByRefno (args.refno(), req) ;
   hgassert (rv, "TIOINETFactoryDirect::connected(): could not remove from map") ;
   hgassert (req, "TIOINETFactoryDirect::connected(): nil request") ;
   hgassert (req.ptr()->user(), "TIOINETFactoryDirect::connected(): nil request user") ;
   
   TIOINETResponsePtr res ;
   const INETAddress* thisaddr = conn.ptr()->thisAddress() ;
   if (! thisaddr) {
      DEBUGNL ("TIOINETFactoryDirect::connected(): "
               "could not get one of this and that endpoint\'s addresses") ;
      res = TIOINETResponsePtr (HGNEW (TIOINETResponse (req.ptr(), TIOINETFactory::TIFNOADDR))) ;
   }
   else {
      // important: reuse the req's peer address since WinNT cannot
      // handle an immediate getpeername() on a socket which has just
      // got connected nonblockingly. btw, I save a system call that
      // way. (anyway, NT sucks)
      TIOINETAttributes a (*thisaddr, req.ptr()->address()) ;
      res = 
            TIOINETResponsePtr (
               HGNEW (TIOINETResponse (
                  req.ptr(), 
                  TransparentIOPtr (HGNEW (
                     TransparentSocketIO (conn.ptr()))), a))) ;
   }

   req.ptr()->getUser()->tioInetResponse (res) ;
}

void TIOINETFactoryDirect :: connectError (const ConnectArgs& args) {
   DEBUGNL ("TIOINETFactoryDirect::connectError() ("<<args.address().host()<<
            ", "<<args.address().port()<<')') ;
   
   TIOINETRequestPtr req ;
   bool rv = ref_req_->removeByRefno (args.refno(), req) ;
   hgassert (rv, "TIOINETFactoryDirect::connectError(): could not remove from map") ;
   hgassert (req, "TIOINETFactoryDirect::connectError(): nil request") ;
   hgassert (req.ptr()->user(), "TIOINETFactoryDirect::connectError(): nil request user") ;

   req.ptr()->getUser()->tioInetResponse (
      TIOINETResponsePtr (HGNEW (TIOINETResponse (req.ptr(), TIOINETFactory::TIFNOCONN)))) ;
}

void TIOINETFactoryDirect :: do_cancel_(const TIOINETRequest* req) {
   DEBUGNL ("TIOINETFactoryDirect::do_cancel_("<<req<<')') ;
   long refno ;
   bool rv = ref_req_->removeByRequest (req, refno) ;
   hgassert  (rv, "TIOINETFactoryDirect::do_cancel_(): not found in my map") ;
   rv = Connecter::instance().endRequest (this, refno) ;
   hgassert (rv, "TIOINETFactoryDirect::do_cancel_(): "
             "could not cancel from Connecter::instance()") ;
}





