#ifndef CROMP_CLIENT_CLASS
#define CROMP_CLIENT_CLASS

/*****************************************************************************\
*                                                                             *
*  Name   : cromp_client                                                      *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*  Purpose:                                                                   *
*                                                                             *
*    Supplies primitive operations for requesting services of a CROMP-based   *
*  server application.  Tentacles in cromp clients are only used for          *
*  restoring original objects, but they are required for that.  Keep in mind  *
*  that for communication to be possible, a cromp server and its cromp client *
*  must possess tentacles that grok the same infotons.  They do not need to   *
*  be the same objects and probably shouldn't be.  It makes sense to          *
*  implement an unpacking / cloning tentacle and then derive the full-service *
*  request processing tentacle from it.                                       *
*                                                                             *
*******************************************************************************
* Copyright (c) 2000-$now By Author.  This program is free software; you can  *
* redistribute it and/or modify it under the terms of the GNU General Public  *
* License as published by the Free Software Foundation; either version 2 of   *
* the License or (at your option) any later version.  This is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

#include "cromp_common.h"

// forward:
class asynch_connection_thread;
class blank_entity_registry;
class blowfish_crypto;
class encryption_tentacle;
class encryption_wrapper;
class int_roller;
class internet_address;
class time_stamp;

class CROMP_CLASS_STYLE cromp_client : public cromp_common
{
public:
  enum constraints {
    DEFAULT_MAX_CONNECT_WAIT = 28 * SECOND_ms,
      // the server had better connect within this time limit or it will
      // be considered missing in action.
    DEFAULT_MAX_RESPONSE_WAIT = 4 * MINUTE_ms
      // a server should be able to answer in this interval or something is
      // really wrong with the system.
  };

  cromp_client(const internet_address &destination,
          int connection_wait = DEFAULT_MAX_CONNECT_WAIT,
          int max_per_ent = DEFAULT_MAX_ENTITY_QUEUE);
    // will connect to a cromp_server on the host specified by "destination".

  virtual ~cromp_client();

  IMPLEMENT_CLASS_NAME("cromp_client");

  const octopus_entity &entity() const;
    // returns our identity within the octopus server.

  void enable_encryption();
    // this turns on the encryption.  this should be done before the first
    // connect or login invocations.  once it's enabled, it stays enabled.

  void reset(const internet_address &destination,
          int connection_wait = DEFAULT_MAX_CONNECT_WAIT,
          int max_per_ent = DEFAULT_MAX_ENTITY_QUEUE);
    // disconnects from any previous server and reconstructs this client.
    // the client will be left in an unconnected and unauthenticated state.
    // any pre-existing tentacles will still be hooked up to the object.
    // use connect() to re-establish the connection to the server.

  outcome connect();
    // attempts to connect to the server.  OKAY is returned if this succeeds.

  outcome asynch_connect();
    // this is a non-blocking connect.  it behaves like connect(), but should
    // never take more than a few milliseconds to return.  if the client is
    // already connected, then nothing is done.  otherwise no operations will
    // be permitted until the reconnection has succeeded.  a call to regular
    // connect() cancels the asynchronous connect.

  bool connected() const;
    // returns true if we think we are connected to the server.

  outcome disconnect();
    // disconnects from the cromp server and releases all connection resources.

  virtual outcome login();
    // attempts to log in to the server.  we must already have connected
    // when this is called.

  //////////////////////////////////////////////////////////////////////////

  outcome submit(const infoton &request, octopus_request_id &item_id,
          int max_tries = 80);
    // requests a transaction from the cromp_server described by "request".
    // the return value is OKAY if the request was successfully sent or it
    // will be another outcome that indicates a failure of transmission.
    // the "max_tries" is the number of times to try getting the send out;
    // if asynchronous sends are allowed to accumulate, then 1 works here.

  outcome acquire(infoton * &response, const octopus_request_id &cmd_id,
          int timeout = DEFAULT_MAX_RESPONSE_WAIT);
    // attempts to receive a "response" to a previously submitted request
    // with the "cmd_id".  if "timeout" is non-zero, then the response will
    // be awaited for "timeout" milliseconds.  otherwise the function returns
    // immediately.  note that "response" will only be generated properly given
    // a tentacle that knows the infoton type received.  this requires the
    // cromp_client to have tentacles registered for all of the types to be
    // exchanged.  this can be used for asynchronous retrieval if the timeout
    // is zero and one knows a previously requested "cmd_id".

  //////////////////////////////////////////////////////////////////////////

  // grittier functions...

  outcome synchronous_request(const infoton &to_send, infoton * &received,
          octopus_request_id &item_id,
	  int timeout = DEFAULT_MAX_RESPONSE_WAIT);
    // submits the infoton "to_send" and waits for the reply.  if there is
    // a reply in the allotted time, then "received" is set to that.  the
    // "item_id" is set to the request id assigned to the request.

  octopus_request_id next_id();
    // generates the next identifier.  this can be used as a unique identifier
    // for derived objects since the id is issued from our server.  it is
    // not unique across different types of cromp servers though, nor across
    // cromp servers running on different hosts.

  void decrypt_package_as_needed(outcome &to_return, infoton * &response,
          const octopus_request_id &cmd_id);
    // this ensures that if the "response" is encrypted, it will be decrypted
    // and replaced with the real object intended.  this method is invoked
    // automatically by acquire(), but if the cromp_common retrieve methods are
    // used directly, this should be done to the response before using it.

  void keep_alive_pause(int duration = 0, int interval = 40);
    // pauses this thread but keeps calling into the cromp support to ensure
    // items get delivered or received when possible.  the call will snooze
    // for at least "duration" milliseconds with individual sleeps being
    // allowed the "interval" milliseconds.

private:
  bool _encrypting;  // true if this connection should be encrypted.
  int _connection_wait;  // the length of time we'll allow a connection.
  mutex *_lock;  // protects our data members below.
  octopus_entity *_ent;  // our identity within the octopus server.
  int_roller *_req_id;  // the numbering of our nth request is depicted here.
  bool _identified;  // true if our initial identifier verification succeeded.
  bool _authorized;  // true if our login was successful.
  bool _disallowed;  // nothing is allowed right now except asynch connecting.
  friend class asynch_connection_thread;  // solely so it can use r_p_c method.
  asynch_connection_thread *_asynch_connector;  // b-ground connection thread.
#ifndef OMIT_CRYPTO_SUPPORT
  bool _channel_secured;  // true if an encrypted connection has been made.
  blowfish_crypto *_crypto;  // tracks our key, once we have one.
  encryption_tentacle *_encrypt_arm;  // processes encryption for us.
#endif
  blank_entity_registry *_guardian;  // simple security support.

  void stop_asynch_thread();  // stops the background connector.

  outcome locked_connect();
    // called by the other types of connection processes to get the work done.

  octopus_entity randomize_entity() const;
    // provides a junk entity for our temporary identifier that we'll use
    // until the server gives us a new one.

  outcome locked_disconnect();
    // assumes that the client is locked.  this is the real disconnect.

#ifndef OMIT_CRYPTO_SUPPORT
  bool wrap_infoton(const infoton &request, encryption_wrapper &wrapped);
    // wraps an infoton for sending over an encrypted connection.
#endif
};

#endif

