/*
 *    SCCS ID: %Z%%M% %I% %E% SST 
 *
 *    Copyright (c) 1992-1993 Mark Soloway.  All rights reserved.
 *    See SST_LEGAL_NOTICE file for terms of the license.
 */

#include "PollFD.h"
#include "TTConstants.h"
#include "TTFile.h"
#include "TTMessage.h"
#include "TTPattern.h"
#include "TTSession.h"

#include <stdlib.h>

extern int errno;

TTSession::TTSession (Tt_status &status)
{
  setTTSessionDefaults ();

  char *session_id;
  char *proc_id = tt_open ();
  status = tt_ptr_error (proc_id);

  if (status == TT_OK) {
    session_id = tt_default_session ();
    status = tt_ptr_error (session_id);
  }

  if (status == TT_OK) {
    ttFD = tt_fd ();
    ttProcID = strcpy (new char [strlen (proc_id) + 1], proc_id);
    ttSessionID = strcpy (new char [strlen (session_id) + 1], session_id);

    pollFD = new PollFD (ttFD);
  }
}

TTSession::TTSession (Tt_session_type  session_type,
		      const char      *session_hint,
		      Tt_status       &status)
{
  const char *display;

  setTTSessionDefaults ();

  switch (session_type) {
  case TT_DEFAULT_SESSION:
    break;
    
  case TT_INITIAL_SESSION:
    (void)tt_default_session_set (tt_initial_session ());
    break;
    
  case TT_USER_SESSION:
    (void)tt_default_session_set (session_hint);
    break;
    
  case TT_X_SESSION:
    if (!(display = session_hint))
      display = getenv ("DISPLAY");

    (void)tt_default_session_set (tt_X_session (display));
    break;
    
  default:
    break;
  }
  
  char *session_id;
  char *proc_id = tt_open ();
  status = tt_ptr_error (proc_id);

  if (status == TT_OK) {
    session_id = tt_default_session ();
    status = tt_ptr_error (session_id);
  }

  if (status == TT_OK) {
    ttFD = tt_fd ();
    ttProcID = strcpy (new char [strlen (proc_id) + 1], proc_id);
    ttSessionID = strcpy (new char [strlen (session_id) + 1], session_id);

    pollFD = new PollFD (ttFD);
  }
}

TTSession::TTSession (const TTSession &session)
{
  copy (session);
}

TTSession &TTSession::operator= (const TTSession &session)
{
  if (this != &session) {
    destroy ();
    copy (session);
  }

  return *this;
}

void TTSession::setTTSessionDefaults ()
{
  pollErrorCallback = (TTPollCallback)NULL;
  sessionCallback = (TTSessionCallback)NULL;
  timeOutCallback = (TTPollCallback)NULL;
  pollFD = (PollFD *)NULL;
  timeOut = -1;
  ttDefaultFile = (char *)NULL;
  ttDefaultPType = (char *)NULL;
  ttFD = -1;
  ttProcID = (char *)NULL;
  ttSessionID = (char *)NULL;
  useDefaultSessionCallbackFlag = TT_FALSE;
  useDefaultPollErrorCallbackFlag = TT_FALSE;
  useDefaultTimeOutCallbackFlag = TT_FALSE;
}

TTSession::~TTSession ()
{
  destroy ();
}

Tt_status TTSession::setDefaultFile (const char *file)
{
  if (ttDefaultFile) delete ttDefaultFile;
  ttDefaultFile = (file ? strcpy (new char [strlen (file) + 1], file) :
			  (char *)NULL);
  return TT_OK;
}

Tt_status TTSession::setDefaultFile (const TTFile &tt_file)
{
  Tt_status  status;
  char      *file = tt_file.getFile (status);

  if (status == TT_OK) {
    if (ttDefaultFile) delete ttDefaultFile;
    ttDefaultFile = (file ? strcpy (new char [strlen (file) + 1], file) :
			    (char *)NULL);
  }

  if (file) tt_free (file);

  return status;
}

Tt_status TTSession::setDefaultPType (const char *ptype)
{
  if (ttDefaultPType) delete ttDefaultPType;
  ttDefaultPType = (ptype ? strcpy (new char [strlen (ptype) + 1], ptype) :
                            (char *)NULL);
  return TT_OK;  
}

Tt_status TTSession::joinFile (const char *file)
{
  Tt_status status = TT_ERR_FILE;

  if (file) {
    status = setTTDefaults ();

    if (status == TT_OK) {
      if (!fileTable.getItem (file)) {
        fileTable.addItem (file, (TTGeneric *)&dummyData);

        status = tt_file_join (file);

        if (status == TT_OK) {
          if (ttDefaultFile) delete ttDefaultFile;
          ttDefaultFile = strcpy (new char [strlen (file) + 1], file);
        }
      }
      else {
	status = TT_OK;
      }
    }
  }

  return status;
}

Tt_status TTSession::joinFile (const TTFile &tt_file)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    char *file = tt_file.getFile (status);

    if (status == TT_OK)
      if (!file) status = TT_ERR_FILE;

    if (status == TT_OK) {
      if (!fileTable.getItem (file)) {
        fileTable.addItem (file, (TTGeneric *)&dummyData);

        status = tt_file_join (file);

        if (status == TT_OK) {
          if (ttDefaultFile) delete ttDefaultFile;
          ttDefaultFile = strcpy (new char [strlen (file) + 1], file);
        }
      }
      else {
	status = TT_OK;
      }
    }

    if (file) tt_free (file);
  }

  return status;
}

Tt_status TTSession::quitFile (const char *file)
{
  Tt_status status = TT_ERR_FILE;

  if (file) {
    status = setTTDefaults ();
  
    if (status == TT_OK) {
      if (fileTable.getItem (file)) {
        (void)fileTable.removeItem (file);

        if (file && ttDefaultFile && !strcmp (file, ttDefaultFile)) {
	  delete ttDefaultFile;
	  ttDefaultFile = (char *)NULL;
        }

        status = tt_file_quit (file);
      }
      else {
	status = TT_ERR_FILE;
      }
    }
  }

  return status;
}

Tt_status TTSession::quitFile (const TTFile &tt_file)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    char *file = tt_file.getFile (status);

    if (status == TT_OK)
      if (!file) status = TT_ERR_FILE;

    if (status == TT_OK) {
      if (fileTable.getItem (file)) {
        (void)fileTable.removeItem (file);

        if (file && ttDefaultFile && !strcmp (file, ttDefaultFile)) {
	  delete ttDefaultFile;
	  ttDefaultFile = (char *)NULL;
        }

        status = tt_file_quit (file);
      }
      else {
	status = TT_ERR_FILE;
      }
    }
  }
    
  return status;
}

Tt_status TTSession::quitAllFiles ()
{
  Tt_status status = setTTDefaults ();
 
  if (status == TT_OK) {
    char *file;

    TTBoolean reset = TT_TRUE;
    while (fileTable.traverse (reset, file)) {
      Tt_status temp_status = tt_file_quit (file);
      if (status != TT_ERR_FILE)
        status = temp_status;
      reset = TT_FALSE;
    }

    if ((status == TT_OK) || (status == TT_ERR_FILE))
      fileTable.dropItems ();

    if (ttDefaultFile) {
      delete ttDefaultFile;
      ttDefaultFile = (char *)NULL;
    }
  } 

  return status;
}

#if (TT_VERSION >= 10100)
Tt_status TTSession::joinContext (const char *name, int value)
{
  Tt_status status = TT_ERR_SLOTNAME;
 
  if (name) {
    status = setTTDefaults ();
 
    if (status == TT_OK) {
      TTContextValue *context_value = new TTContextValue;
      context_value->setIntValue (value);

      TTContextValueArray *context_value_array = (TTContextValueArray *)
				                 contextTable.getItem (name);

      if (!context_value_array) {
        context_value_array = new TTContextValueArray;
        contextTable.addItem (name, context_value_array);
      }  
      else {
        if (findContext (*context_value_array, *context_value) > -1) {
	  delete context_value;
	  status = TT_OK;
        }
      }

      context_value_array->addItem (context_value);
      status = tt_icontext_join (name, value);
    } 
  } 
 
  return status;
}
#else
Tt_status TTSession::joinContext (const char *, int)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10100)
Tt_status TTSession::joinContext (const char *name, const char *value)
{
  Tt_status status = TT_ERR_SLOTNAME;
 
  if (name) {
    status = setTTDefaults ();

    if (status == TT_OK) {
      TTContextValue *context_value = new TTContextValue;
      context_value->setBytesValue (value);
 
      TTContextValueArray *context_value_array = (TTContextValueArray *)
                                                 contextTable.getItem (name);
 
      if (!context_value_array) {
        context_value_array = new TTContextValueArray; 
        contextTable.addItem (name, context_value_array);
      }  
      else { 
        if (findContext (*context_value_array, *context_value) > -1) {
          delete context_value; 
          status = TT_OK;
        }
      }  
 
      context_value_array->addItem (context_value);
      status = tt_context_join (name, value);
    }    
  }  
 
  return status; 
}
#else
Tt_status TTSession::joinContext (const char *, const char *)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10100)
Tt_status TTSession::joinContext (const char          *name,
				  const unsigned char *value,
				  int                  length)
{
  Tt_status status = TT_ERR_SLOTNAME; 
  
  if (name) {
    status = setTTDefaults (); 
 
    if (status == TT_OK) { 
      TTContextValue *context_value = new TTContextValue;
      context_value->setBytesValue (value, length);
 
      TTContextValueArray *context_value_array = (TTContextValueArray *)
                                                 contextTable.getItem (name); 
  
      if (!context_value_array) { 
        context_value_array = new TTContextValueArray;   
        contextTable.addItem (name, context_value_array); 
      }   
      else {  
        if (findContext (*context_value_array, *context_value) > -1) { 
          delete context_value;  
          status = TT_OK; 
        } 
      }   
  
      context_value_array->addItem (context_value); 
      status = tt_bcontext_join (name, value, length); 
    }     
  }     
  
  return status;  
}
#else
Tt_status TTSession::joinContext (const char *, const unsigned char *, int)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10101)
Tt_status TTSession::joinContext (const char *name,
				  xdrproc_t   xdr_proc,
				  void	     *value)
{
  Tt_status status = TT_ERR_SLOTNAME;

  if (name) {
    (void)setTTDefaults (); 

    TTMessage temp_message;
    status = temp_message.addArg (TT_INOUT, (char *)NULL, xdr_proc, value);

    if (status == TT_OK) {
      unsigned char *value;
      int            length;

      status = temp_message.getArgValue (0, value, length);

      if (status == TT_OK) {
        status = joinContext (name, value, length);
      }
    }
  }

  return status;
}
#else
Tt_status TTSession::joinContext (const char *, xdrproc_t, void	*)
{
  return TT_ERR_UNIMP;
}
#endif 

#if (TT_VERSION >= 10100)
Tt_status TTSession::quitContext (const char *name, int value)
{
  Tt_status status = TT_ERR_SLOTNAME;

  if (name) {
    status = setTTDefaults ();
 
    if (status == TT_OK) {
      TTContextValueArray *context_value_array = (TTContextValueArray *)
					         contextTable.getItem (name);

      if (context_value_array) {
	TTContextValue context_value;
	context_value.setIntValue (value);

	int index = findContext (*context_value_array, context_value);
	if (index > -1) {
	  delete context_value_array->removeItem (index);
          status = tt_icontext_quit (name, value);
	}
	else {
	  status = TT_ERR_SLOTNAME;
	}

	if (!context_value_array->getSize ()) {
	  delete contextTable.removeItem (name);
	}
      } 
      else {
	status = TT_ERR_SLOTNAME;
      }
    }
  } 
 
  return status; 
}
#else
Tt_status TTSession::quitContext (const char *, int)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10100)
Tt_status TTSession::quitContext (const char *name, const char *value)
{
  Tt_status status = TT_ERR_SLOTNAME;
 
  if (name) {
    status = setTTDefaults ();
 
    if (status == TT_OK) {
      TTContextValueArray *context_value_array = (TTContextValueArray *)
						 contextTable.getItem (name);
 
      if (context_value_array) {
        TTContextValue context_value; 
        context_value.setBytesValue (value);
 
        int index = findContext (*context_value_array, context_value);
        if (index > -1) { 
          delete context_value_array->removeItem (index);
          status = tt_context_quit (name, value);
        } 
        else {   
          status = TT_ERR_SLOTNAME; 
        } 
 
        if (!context_value_array->getSize ()) { 
          delete contextTable.removeItem (name);
        }
      } 
      else {
	status = TT_ERR_SLOTNAME;
      }
    }   
  }    
    
  return status; 
}
#else
Tt_status TTSession::quitContext (const char *, const char *)  
{
  return TT_ERR_UNIMP;
}
#endif   

#if (TT_VERSION >= 10100)
Tt_status TTSession::quitContext (const char          *name,
				  const unsigned char *value,
				  int                  length)
{
  Tt_status status = TT_ERR_SLOTNAME;
 
  if (name) {
    status = setTTDefaults ();
 
    if (status == TT_OK) {
      TTContextValueArray *context_value_array = (TTContextValueArray *)
						 contextTable.getItem (name);
 
      if (context_value_array) {
        TTContextValue context_value; 
        context_value.setBytesValue (value, length);
 
        int index = findContext (*context_value_array, context_value);
        if (index > -1) { 
          delete context_value_array->removeItem (index);
          status = tt_bcontext_quit (name, value, length);
        }
        else {   
          status = TT_ERR_SLOTNAME; 
        }
 
        if (!context_value_array->getSize ()) { 
          delete contextTable.removeItem (name);
        }
      } 
      else {
	status = TT_ERR_SLOTNAME;
      }
    }   
  }    
    
  return status; 
}  
#else
Tt_status TTSession::quitContext (const char *, const unsigned char *, int)
{
  return TT_ERR_UNIMP;
}
#endif   

#if (TT_VERSION >= 10101) 
Tt_status TTSession::quitContext (const char *name,
                                  xdrproc_t   xdr_proc,
                                  void       *value)  
{ 
  Tt_status status = TT_ERR_SLOTNAME; 
 
  if (name) {
    (void)setTTDefaults ();
 
    TTMessage temp_message; 
    status = temp_message.addArg (TT_INOUT, (char *)NULL, xdr_proc, value); 
 
    if (status == TT_OK) { 
      unsigned char *value; 
      int            length; 
 
      status = temp_message.getArgValue (0, value, length);
 
      if (status == TT_OK) { 
        status = quitContext (name, value, length); 
      }  
    } 
  } 
 
  return status;
}
#else
Tt_status TTSession::quitContext (const char *, xdrproc_t, void *)
{
  return TT_ERR_UNIMP;
}
#endif   
 
#if (TT_VERSION >= 10100)
Tt_status TTSession::quitAllContextValues (const char *name)
{
  Tt_status status = TT_ERR_SLOTNAME;
 
  if (name) {
    status = setTTDefaults ();
 
    if (status == TT_OK) {
      TTContextValueArray *context_value_array = (TTContextValueArray *)
						 contextTable.getItem (name);
 
      if (context_value_array) {
	TTContextValue *context_value;
	Tt_status       temp_status;

	while (context_value = context_value_array->removeItem (0)) {
	  if (context_value->getType () == TT_INT_VALUE) {
	    temp_status = tt_icontext_quit (name,
				            context_value->getIntValue ());
	  }
	  else {
	    temp_status = tt_bcontext_quit (name,
					    context_value->getBytesValue (),
					    context_value->getLength ());
	  }

	  if (temp_status != TT_ERR_SLOTNAME)
	    status = temp_status;

	  if (status == TT_OK)
	    delete context_value;
	}
 
	if (status == TT_OK)
          delete contextTable.removeItem (name);
      }
      else {
	status = TT_ERR_SLOTNAME;
      }
    }   
  }    
    
  return status;
}  
#else
Tt_status TTSession::quitAllContextValues (const char *)
{
  return TT_ERR_UNIMP;
}
#endif   

Tt_status TTSession::declarePType (const char *ptype)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_ptype_declare (ptype);

    if (status == TT_OK) {
      if (ttDefaultPType) {
        delete ttDefaultPType;
	ttDefaultPType = strcpy (new char [strlen (ptype) + 1], ptype);
      }

      return rejoin ();
    }
  }

  return status;
}

#if (TT_VERSION >= 10100)
Tt_status TTSession::undeclarePType (const char *ptype)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_ptype_undeclare (ptype);

    if (status == TT_OK) {
      if (ptype && ttDefaultPType && !strcmp (ptype, ttDefaultPType)) {
        delete ttDefaultPType;
	ttDefaultPType = (char *)NULL;
      }
    }
  }

  return status;
}
#else
Tt_status TTSession::undeclarePType (const char *)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10100)
Tt_status TTSession::doesPTypeExist (const char *ptype)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_ptype_exists (ptype);
  }

  return status;
}
#else
Tt_status TTSession::doesPTypeExist (const char *)
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10101)
Tt_status TTSession::loadPTypesFile (const char *file)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) { 
    status = tt_session_types_load (ttSessionID, file);
  }

  return status; 
}
#else
Tt_status TTSession::loadPTypesFile (const char *)
{
  return TT_ERR_UNIMP;
}
#endif 

Tt_status TTSession::setTimeOut (int time_out)
{
  timeOut = time_out;
  return TT_OK;
}

Tt_status TTSession::setTimeOutCallback (TTPollCallback callback)
{
  timeOutCallback = callback;
  return TT_OK;
}

Tt_status TTSession::setPollErrorCallback (TTPollCallback callback)
{
  pollErrorCallback = callback;
  return TT_OK;
}

TTMessage *TTSession::waitForMessage (Tt_status &status)
{
  status = setTTDefaults ();
  TTMessage *message = (TTMessage *)NULL;;

  if (status == TT_OK) {
    pollFD->setTimeOut (timeOut);

    int poll_status;
    for (;;) {
      if ((poll_status = pollFD->waitForActiveFD ()) > 0) {
	Tt_message tt_message = tt_message_receive ();
	if (!tt_message) continue;
	status = tt_ptr_error (tt_message);

	if (status == TT_OK) {
	  message = new TTMessage (tt_message);
	  if (useDefaultSessionCallbackFlag)
	    status = defaultSessionCallback (message);
	  else if (sessionCallback)
	    status = (*sessionCallback) (this, message);
	  break;
	}
	else
	  (void)tt_message_destroy (tt_message);
      }
      else if ((poll_status == 0) || (errno == 0)) {
	Tt_poll_action action = TT_POLL_ABORT;

        if (useDefaultTimeOutCallbackFlag)
          action = defaultTimeOutCallback ();
        else if (timeOutCallback)
          action = (*timeOutCallback) (this);

        if (action == TT_POLL_ABORT) break;
      }
      else {
	Tt_poll_action action = TT_POLL_ABORT;

        if (useDefaultPollErrorCallbackFlag)
          action = defaultPollErrorCallback ();
        else if (pollErrorCallback)
          action = (*pollErrorCallback) (this);

        if (action == TT_POLL_ABORT) break;
      }
    }
  }

  return message;
}

TTMessage *TTSession::waitForServiceRequest (Tt_status &status)
{
  status = setTTDefaults ();
  TTMessage *message = (TTMessage *)NULL;

  if (status == TT_OK) {
    pollFD->setTimeOut (timeOut);

    int poll_status;
    for (;;) {
      if ((poll_status = pollFD->waitForActiveFD ()) > 0) {
	Tt_message tt_message = tt_message_receive ();
	if (!tt_message) continue;
	status = tt_ptr_error (tt_message);
	
	if (status == TT_OK) {
	  if (tt_message_status (tt_message) == TT_WRN_START_MESSAGE) {
#if (TT_VERSION >= 10100) || !sun
	    if (tt_message_accept (tt_message) != TT_OK) {
	      (void) tt_message_fail (tt_message);
	      (void) tt_message_destroy (tt_message);
	      continue;
	    }
#else
	    (void) tt_message_fail (tt_message);
	    (void) tt_message_destroy (tt_message);
	    continue;
#endif
	  }
	
	  message = new TTMessage (tt_message);
	  if (useDefaultSessionCallbackFlag)
	    status = defaultSessionCallback (message);
	  else if (sessionCallback)
	    status = (*sessionCallback) (this, message);
	  break;
	}
	else
	  (void)tt_message_destroy (tt_message);
      }
      else if ((poll_status == 0) || (errno == 0)) {
	Tt_poll_action action = TT_POLL_ABORT;

	if (useDefaultTimeOutCallbackFlag)
	  action = defaultTimeOutCallback ();
	else if (timeOutCallback)
	  action = (*timeOutCallback) (this);

        if (action == TT_POLL_ABORT) break;
      }
      else {
	Tt_poll_action action = TT_POLL_ABORT;

        if (useDefaultPollErrorCallbackFlag)
          action = defaultPollErrorCallback (); 
        else if (pollErrorCallback) 
          action = (*pollErrorCallback) (this);
 
        if (action == TT_POLL_ABORT) break;
      }  
    }
  }

  return message;
}

Tt_status TTSession::sendSynchronousMessage (TTMessage &message)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = message.refresh ();
  }

  if (status == TT_OK) {
    Tt_message tt_message = message.getMessage ();
    (void) tt_message_class_set (tt_message, TT_REQUEST);
    status = tt_message_send (tt_message);

    if (status == TT_OK) {
      message.setSentFlag (TT_TRUE);
      pollFD->setTimeOut (timeOut);

      int       poll_status;
      TTBoolean reply_received = TT_FALSE;
      while (!reply_received) {
	if ((poll_status = pollFD->waitForActiveFD ()) > 0) {
	  Tt_message reply = tt_message_receive ();
	  if (!reply || (reply != tt_message)) continue;
	  status = tt_ptr_error (reply);
	  
	  if (status == TT_OK) {
	    switch (tt_message_state (reply)) {
	    case TT_HANDLED:
	      reply_received = TT_TRUE;
	      break;
	    case TT_STARTED:
	      break;
	    case TT_FAILED:
	      reply_received = TT_TRUE;
	      break;
	    default:
	      break;
	    }  
	  }
	  else
	    reply_received = TT_TRUE;
	}
        else if ((poll_status == 0) || (errno == 0)) {
	  Tt_poll_action action = TT_POLL_ABORT;

	  if (useDefaultTimeOutCallbackFlag)
	    action = defaultTimeOutCallback ();
	  else if (timeOutCallback)
	    action = (*timeOutCallback) (this);
 
          if (action == TT_POLL_ABORT) reply_received = TT_TRUE;
        }
        else {
	  Tt_poll_action action = TT_POLL_ABORT;

          if (useDefaultPollErrorCallbackFlag)
            action = defaultPollErrorCallback (); 
          else if (pollErrorCallback) 
            action = (*pollErrorCallback) (this);

          if (action == TT_POLL_ABORT) reply_received = TT_TRUE;
        }  
      }
    }
  }

  return status;
}

Tt_status TTSession::sendAsynchronousMessage (TTMessage &message)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = message.refresh ();
  }

  if (status == TT_OK) {
    if (message.getMessage ()) {
      status = tt_message_send (message.getMessage ());
      message.setSentFlag (TT_TRUE);
    }
  }

  return status;
}

#if (TT_VERSION >= 10100)
Tt_status TTSession::sendAsynchronousMessage (TTMessage &message,
					      char*     &message_send_id)
{
  Tt_status status = sendAsynchronousMessage (message);
 
  message_send_id = (char *)NULL;
  if (status == TT_OK) {
    Tt_message tt_message = message.getMessage ();
    message_send_id = tt_message_id (tt_message);
    status = tt_ptr_error (message_send_id);
  }

  return status;
}
#else
Tt_status TTSession::sendAsynchronousMessage (TTMessage&, char*&)
{
  return TT_ERR_UNIMP;
}
#endif

TTMessage *TTSession::receiveAsynchronousMessage (Tt_status &status)
{
  status = setTTDefaults ();
  TTMessage *message = (TTMessage *)NULL;

  if (status == TT_OK) {
    Tt_message tt_message = tt_message_receive ();
    if (tt_message) {
      status = tt_ptr_error (tt_message);
      if (status == TT_OK) {
	Tt_class tt_class = tt_message_class (tt_message);
	status = tt_int_error(tt_class);
	if (status == TT_OK) {
	  if (tt_class == TT_REQUEST) {
	    message = (TTMessage *)tt_message_user (tt_message, TT_OBJECT_KEY);
	    if (!message || (tt_ptr_error (message) != TT_OK))
	      message = new TTMessage (tt_message);
	  }
	  else {
	    message = new TTMessage (tt_message);
	  }

          if (useDefaultSessionCallbackFlag)
            status = defaultSessionCallback (message);
          else if (sessionCallback)
            status = (*sessionCallback) (this, message);
	}
      }
    }
  }

  return message;
}

Tt_status TTSession::sendServiceRequest (TTMessage &message)
{
  Tt_status status = setTTDefaults ();
 
  if (status == TT_OK) {
    status = message.refresh ();
  }

  if (status == TT_OK) {
    Tt_message tt_message = message.getMessage ();
    (void) tt_message_class_set (tt_message, TT_REQUEST);
    status = tt_message_send (tt_message);
    
    if (status == TT_OK) {
      message.setSentFlag (TT_TRUE);
      pollFD->setTimeOut (timeOut);
 
      int       poll_status;
      TTBoolean reply_received = TT_FALSE;
      TTBoolean handler_started = TT_FALSE;
      int       counter = 0;
      while ((status == TT_OK) && !reply_received) {
	counter++;

	if ((poll_status = pollFD->waitForActiveFD ()) > 0) {
	  Tt_message reply = tt_message_receive ();
	  if (!reply || (reply != tt_message)) continue;
	  status = tt_ptr_error (reply);
 
	  if (status == TT_OK) {
	    switch (tt_message_state (reply)) {
	    case TT_HANDLED:
	      reply_received = TT_TRUE;
	      break;
	    case TT_STARTED:
	      handler_started = TT_TRUE;
	      break;
	    case TT_FAILED:
	      if (handler_started || (counter == 1)) {
		handler_started = TT_FALSE;
		status = message.refresh ();

		if (status == TT_OK) {
		  tt_message = message.getMessage ();
		  status = tt_message_send (tt_message);

		  if (status == TT_OK) {
		    message.setSentFlag (TT_TRUE);
		  }
		}
	      }
	      else
		reply_received = TT_TRUE;
	      break;
	    default:
	      break;
	    }
	  }
	  else
	    reply_received = TT_TRUE;
	}    
        else if ((poll_status == 0) || (errno == 0)) {
	  Tt_poll_action action = TT_POLL_ABORT;

          if (useDefaultTimeOutCallbackFlag)
            action = defaultTimeOutCallback ();
          else if (timeOutCallback)
            action = (*timeOutCallback) (this);
 
          if (action == TT_POLL_ABORT) reply_received = TT_TRUE; 
        }
        else {
	  Tt_poll_action action = TT_POLL_ABORT;

          if (useDefaultPollErrorCallbackFlag)
            action = defaultPollErrorCallback (); 
          else if (pollErrorCallback) 
            action = (*pollErrorCallback) (this);
 
          if (action == TT_POLL_ABORT) reply_received = TT_TRUE; 
        }  
      }
    }   
  }

  return status;
}

Tt_status TTSession::replyToMessage (const TTMessage &message) const
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    return tt_message_reply (message.getMessage ());
  }

  return status;
}

Tt_status TTSession::rejectMessage (const TTMessage &message) const
{ 
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_message_reject (message.getMessage ()); 
  }

  return status;
} 

Tt_status TTSession::failMessage (const TTMessage &message,
				  Tt_status        message_status,
				  const char      *message_status_string) const
{ 
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    Tt_message tt_message = message.getMessage ();
    (void)tt_message_status_set (tt_message, message_status);
    (void)tt_message_status_string_set (tt_message, message_status_string);
    status = tt_message_fail (tt_message); 
  }

  return status;
} 

#if (TT_VERSION >= 10100) || !sun
Tt_status TTSession::acceptMessage (const TTMessage &message) const
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_message_accept (message.getMessage ());
  }

  return status;
}
#else
Tt_status TTSession::acceptMessage (const TTMessage&) const
{
  return TT_ERR_UNIMP;
}
#endif

#if (TT_VERSION >= 10100)
Tt_status TTSession::sendMessageOnExit (TTMessage &message)
{
  Tt_status status = setTTDefaults ();

  status = message.refresh ();
  if (status == TT_OK) {
    Tt_message tt_message = message.getMessage ();
    status = tt_message_send_on_exit (tt_message);
  }

  return status;
}
#else
Tt_status TTSession::sendMessageOnExit (TTMessage&)
{
  return TT_ERR_UNIMP;
}
#endif

Tt_status TTSession::registerPattern (const TTPattern &pattern)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_pattern_register (pattern.getPattern ());

    if (status == TT_OK) {
      status = rejoin ();
    }
  }

  return status;
}

Tt_status TTSession::unregisterPattern (const TTPattern &pattern)
{
  Tt_status status = setTTDefaults ();

  if (status == TT_OK) {
    status = tt_pattern_unregister (pattern.getPattern ());
  }

  return status;
}

Tt_status TTSession::setCallback (TTSessionCallback callback)
{
  sessionCallback = callback;
  return TT_OK;
}

int TTSession::getPropertyNameCount (Tt_status &status) const
{
  int count = tt_session_propnames_count (ttSessionID);
  status = tt_int_error (count);
  return count;
}

char *TTSession::getPropertyName (int index, Tt_status &status) const
{
  char *name = tt_session_propname (ttSessionID, index);
  status = tt_ptr_error (name);
  return name;
}

int TTSession::getPropertyCount (const char *name, Tt_status &status) const
{
  int count = tt_session_prop_count (ttSessionID, name);
  status = tt_int_error (count);
  return count;
}

Tt_status TTSession::getProperty (const char *name,
                                  int         index,
                                  char*      &property) const
{
  property = tt_session_prop (ttSessionID, name, index);
  return tt_ptr_error (property);
}

Tt_status TTSession::getProperty (const char     *name,
                                  int             index,
                                  unsigned char* &property,
                                  int            &length) const
{
  return tt_session_bprop (ttSessionID, name, index, &property, &length);
}
 
Tt_status TTSession::setProperty (const char *name, const char *property)
{
  return tt_session_prop_set (ttSessionID, name, property);
}
 
Tt_status TTSession::setProperty (const char          *name,
                                  const unsigned char *property,
                                  int                  length)
{
  return tt_session_bprop_set (ttSessionID, name, property, length);
}
 
Tt_status TTSession::addProperty (const char *name, const char *property)
{
  return tt_session_prop_add (ttSessionID, name, property);
}
 
Tt_status TTSession::addProperty (const char          *name,
                                  const unsigned char *property,
                                  int                  length)
{
  return tt_session_bprop_add (ttSessionID, name, property, length);
}
 
int TTSession::getFD (Tt_status &status) const
{
  status = TT_OK;
  return ttFD;
}

char *TTSession::getProcID (Tt_status &status) const
{
  status = TT_OK;
  return (strcpy (new char [strlen (ttProcID) + 1], ttProcID));
}

char *TTSession::getSessionID (Tt_status &status) const
{
  status = TT_OK;
  return (strcpy (new char [strlen (ttSessionID) + 1], ttSessionID));
}

char *TTSession::getDefaultFile (Tt_status &status) const
{
  status = TT_OK;
  return (strcpy (new char [strlen (ttDefaultFile) + 1], ttDefaultFile));
}

char *TTSession::getDefaultPType (Tt_status &status) const
{
  status = TT_OK;
  return (strcpy (new char [strlen (ttDefaultPType) + 1], ttDefaultPType));
}

Tt_status TTSession::useDefaultPollErrorCallback (TTBoolean flag)
{
  useDefaultPollErrorCallbackFlag = flag;
  return TT_OK;
}

Tt_poll_action TTSession::defaultPollErrorCallback ()
{
  return TT_POLL_ABORT;
}

Tt_status TTSession::useDefaultSessionCallback (TTBoolean flag)
{
  useDefaultSessionCallbackFlag = flag;
  return TT_OK;
}

Tt_status TTSession::defaultSessionCallback (TTMessage * /* message */)
{
  return TT_OK;
}

Tt_status TTSession::useDefaultTimeOutCallback (TTBoolean flag)
{
  useDefaultTimeOutCallbackFlag = flag;
  return TT_OK;
}

Tt_poll_action TTSession::defaultTimeOutCallback ()
{
  return TT_POLL_ABORT;
}

void TTSession::copy (const TTSession &session)
{
  int mark = tt_mark ();

  setTTSessionDefaults ();
  (void)tt_default_session_set (session.ttSessionID);
  
  char *proc_id = tt_open ();
  Tt_status status = tt_ptr_error (proc_id);
  
  char *session_id;
  if (status == TT_OK) {
    session_id = tt_default_session ();
    status = tt_ptr_error (session_id);
  }
  
  if (status == TT_OK) {
    ttFD = tt_fd ();
    ttProcID = strcpy (new char [strlen (proc_id) + 1], proc_id);
    ttSessionID = strcpy (new char [strlen (session_id) + 1], session_id);

    if (session.ttDefaultFile) {
      ttDefaultFile = strcpy (new char [strlen (session.ttDefaultFile) + 1],
			      session.ttDefaultFile);
    }

    if (session.ttDefaultPType) {
      ttDefaultPType = strcpy (new char [strlen (session.ttDefaultPType) + 1],
                               session.ttDefaultPType);
    }

    TTBoolean reset;

#if (TT_VERSION >= 10100)
    char                *name;
    TTContextValue      *new_value;
    TTContextValueArray *new_value_array;
    TTContextValueArray *value_array;
    TTGenericHash        temp_context_table = session.contextTable;

    reset = TT_TRUE;
    while (value_array = (TTContextValueArray *)
			 temp_context_table.traverse (reset, name)) {
      new_value_array = new TTContextValueArray;
      contextTable.addItem (name, new_value_array);

      int num_values = value_array->getSize ();
      for (int i=0; i < num_values; i++) {
	new_value = new TTContextValue (*(*value_array) [i]);
	new_value_array->addItem (new_value);
      }

      reset = TT_FALSE;
    }
#endif
 
    char          *file;
    TTGenericHash  temp_file_table = session.fileTable;

    reset = TT_TRUE;
    while (temp_file_table.traverse (reset, file) && (status == TT_OK)) {
      fileTable.addItem (file, (TTGeneric *)&dummyData);
      reset = TT_FALSE;
    }
    temp_file_table.dropItems ();

    pollFD = new PollFD (ttFD);
    status = rejoin ();
  }

  tt_release (mark);
}

void TTSession::destroy ()
{
  Tt_status status = setTTDefaults ();
#if (TT_VERSION >= 10100) || !sun
  if (status == TT_OK)
    (void)tt_close ();
#endif
  
  if (pollFD) delete pollFD;
  if (ttDefaultFile) delete ttDefaultFile;
  if (ttDefaultPType) delete ttDefaultPType;
  if (ttProcID) delete ttProcID;
  if (ttSessionID) delete ttSessionID;

  fileTable.dropItems ();
}

Tt_status TTSession::rejoin ()
{
  TTBoolean reset;
  Tt_status status = tt_session_join (ttSessionID);

#if (TT_VERSION >= 10100) 
  char                *name;
  TTContextValue      *value;
  TTContextValueArray *value_array;

  reset = TT_TRUE;
  while ((value_array = (TTContextValueArray *)
		        contextTable.traverse (reset, name)) &&
	 (status == TT_OK)) {
    int num_values = value_array->getSize ();
    for (int i=0; i < num_values; i++) {
      value = (*value_array) [i];

      if (value->getType () == TT_INT_VALUE) {
        status = tt_icontext_join (name, value->getIntValue ());
      }
      else {
        status = tt_bcontext_join (name,
				   value->getBytesValue (),
				   value->getLength ());
      }
    }

    reset = TT_FALSE;
  }
#endif
 
  if (status == TT_OK) {
    char *file;

    reset = TT_TRUE;
    while (fileTable.traverse (reset, file) && (status == TT_OK)) {
      status = tt_file_join (file);
      reset = TT_FALSE;
    }
  }

  return status;
}

Tt_status TTSession::setTTDefaults () const
{
  Tt_status status;

  status = tt_default_procid_set (ttProcID);

  if (status == TT_OK) {
    status = tt_default_session_set (ttSessionID);
  }

  if (status == TT_OK) {
    if (ttDefaultFile) {
      status = tt_default_file_set (ttDefaultFile);
    }
  }

  if (status == TT_OK) {
    if (ttDefaultPType) {
      status = tt_default_ptype_set (ttDefaultPType);
    }
  }  

  return status;
}

#if (TT_VERSION >= 10100)
int TTSession::findContext (const TTContextValueArray &context_value_array,
			    const TTContextValue      &context_value) const
{
  TTContextValue *current_context_value;
  int		  index = -1;
  int             num_values = context_value_array.getSize ();

  for (int i=0; i < num_values; i++) {
    current_context_value = context_value_array [i];

    if (*current_context_value == context_value) {
      index = i;
      break;
    }
  }

  return index;
}
#else
int TTSession::findContext (const TTContextValueArray&,
			    const TTContextValue&) const
{
  return -1;
}
#endif
