// Crude initial attempt at a distributed version of
// matrix-vector product: 
// This is a client that connects to two matrix servers
// holding the same matrix and uses their matrix block_product
// functions.  The call of the first server's block_product 
// function computes the top half of a matrix-vector product
// and the call on the second server does the second
// half.  In this program (unlike an earlier version) 
// these calls are done in parallel, making use of a 
// feature of the Dynamic Invocation Interface.

#pragma warning (disable : 4786)
#include <omniORB2/CORBA.h>
#include <iostream.h>
#include <vector>
using namespace std;

#include "matrix.h"
#include "namebind.cpp"

int main( int argc, char *argv[] )
{
  // ORB initialization
  CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB2");
  CORBA::BOA_var boa = orb->BOA_init(argc, argv, "omniORB2_BOA");

  vector<Matrix_var> client;
  int j;
  for (j = 1; j <= 2; ++j) {
    cout << "j = " << j << endl;
    try {
      CORBA::Object_var obj = getObjectReference(orb, "test", argv[j]);
      cout << "got here" << endl;
      client.push_back(Matrix::_narrow(obj));
      if (CORBA::is_nil(client[j-1])) {
	cerr << "Cannot invoke on a nil object reference.\n" << endl;
	return 1;
      }
      assert(!CORBA::is_nil(client[j - 1]));
    }
    catch(CORBA::COMM_FAILURE& ex) {
      cerr << "Caught system exception COMM_FAILURE, unable to contact the "
	   << "object." << endl;
      return 1;
    }
    catch(omniORB::fatalException& ex) {
      cerr << "Caught omniORB2 fatalException. This indicates a bug is caught "
	   << "within omniORB2.\nPlease send a bug report.\n"
	   << "The exception was thrown in file: " << ex.file() << "\n"
	   << "                            line: " << ex.line() << "\n"
	   << "The error message is: " << ex.errmsg() << endl;
      return 1;
    }
    catch(...) {
      cerr << "Caught a system exception." << endl;
    }
  }
  const int N = 1500;
  Column c;
  c.length(N);
  // Create a column of all 3's.
  for (int i = 0; i < N; ++i) 
    c[i] = 3;
  
  cout << "Computing the matrix-vector product..." << endl;
  vector<Column*> result(2);

  // We want to do the equivalent of
  //   result[0] = client[0]->block_product(0, N/2, c);
  //   result[1] = client[1]->block_product(N/2, N, c);
  // except have them execute in parallel.
  // To achieve this we do the first block_product using the DII
  // and send the request with send_deferred instead of 
  // (the normal) invoke call.  Thus, the first call does
  // not block and we can proceed with the second block_product (calling
  // it in the usual way).

  CORBA::Request_var _req = client[0]->_request( "block_product" );
  _req->add_in_arg( "first" ) <<= (CORBA::ULong)0;
  _req->add_in_arg( "last" ) <<= (CORBA::ULong)(N/2);
  _req->add_in_arg( "c" ) <<= c;
  _req->set_return_type(_tc_Column);
  _req->send_deferred();

  // now asynchrnonously do block_product on second server.
  result[1] = client[1]->block_product(N/2, N, c);

  // The following will wait for the first block_product call to finish:
  _req->get_response();

  if( _req->env()->exception() ) {
    CORBA::Exception *_ex = _req->env()->exception();
    /* Following is from MICO version, needs to be ported to omniORB
   CORBA::UnknownUserException *_uuex = CORBA::UnknownUserException::_downcast( _ex );
    if( _uuex ) {
      mico_throw( CORBA::UNKNOWN() );
    } else {
      mico_throw( *_ex );
    }
    */
  }
  _req->return_value() >>= result[0];

  cout << "got here" << endl;

  // Output the whole result, first half from result[0] followed by 
  // second half from result[1]:
  cout << "The result: " << endl;
  for (j = 0; j < 2; ++j) {
    for (int k = 0; k < result[j]->length(); ++k)
      cout << (*result[j])[k] << " ";
  }
  cout << endl;
  return 0;
}

