/*
 *    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 "TTConstants.h"
#include "TTMessage.h"
#include "TTObjectSpec.h"
#include "TTPattern.h"

#include <stdio.h>

TTPattern::TTPattern (Tt_status &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status);
}

TTPattern::TTPattern (Tt_category category, Tt_status &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  (void)tt_pattern_category_set (ttPattern, category);
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status);
}

TTPattern::TTPattern (Tt_category  category,
		      const char  *op,
		      Tt_scope     scope,
		      Tt_address   address,
		      Tt_status   &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  (void)tt_pattern_category_set (ttPattern, category);
  (void)tt_pattern_op_add (ttPattern, op);
  (void)tt_pattern_scope_add (ttPattern, scope);
  (void)tt_pattern_address_add (ttPattern, address);
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status);
}

TTPattern::TTPattern (Tt_category  category,
		      const char  *op,
		      Tt_scope     scope,
		      Tt_status   &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  (void)tt_pattern_category_set (ttPattern, category);
  (void)tt_pattern_op_add (ttPattern, op);
  (void)tt_pattern_scope_add (ttPattern, scope);
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status); 
}

TTPattern::TTPattern (Tt_category  category,
		      const char  *objid,
		      const char  *op,
		      Tt_scope     scope,
		      Tt_status   &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  (void)tt_pattern_category_set (ttPattern, category);
  (void)tt_pattern_object_add (ttPattern, objid);
  (void)tt_pattern_op_add (ttPattern, op);
  (void)tt_pattern_scope_add (ttPattern, scope);
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status); 
}

TTPattern::TTPattern (Tt_category         category,
                      const TTObjectSpec &spec,
                      const char         *op,
                      Tt_scope            scope,
                      Tt_status          &status)
{
  setTTPatternDefaults ();

  ttPattern = tt_pattern_create ();
  (void)tt_pattern_category_set (ttPattern, category);
  (void)tt_pattern_object_add (ttPattern, spec.getID (status));
  (void)tt_pattern_op_add (ttPattern, op);
  (void)tt_pattern_scope_add (ttPattern, scope);
  status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);

  checkNewPattern (status); 
}

TTPattern::TTPattern (const TTPattern &pattern)
{
  setTTPatternDefaults ();

  patternCallback = pattern.patternCallback;
  ttPattern = pattern.ttPattern;

  increaseReferenceCount ();
}

TTPattern &TTPattern::operator= (const TTPattern &pattern)
{
  if (this != &pattern) {
    if (ttPattern == pattern.ttPattern) {
      setTTPatternDefaults ();

      patternCallback = pattern.patternCallback;
      ttPattern = pattern.ttPattern;
    }
    else {
      decreaseReferenceCount ();
      setTTPatternDefaults ();

      patternCallback = pattern.patternCallback;
      ttPattern = pattern.ttPattern;

      increaseReferenceCount ();
    }
  }

  return *this;
}

void TTPattern::setTTPatternDefaults ()
{
  patternCallback = (TTPatternCallback)NULL;
  ttPattern = (Tt_pattern)NULL;
  useDefaultPatternCallbackFlag = TT_FALSE;
}

TTPattern::~TTPattern ()
{
  decreaseReferenceCount ();
}

Tt_status TTPattern::setCategory (Tt_category category)
{
  return tt_pattern_category_set (ttPattern, category);
}

Tt_category TTPattern::getCategory (Tt_status &status) const
{
  Tt_category category = tt_pattern_category (ttPattern);
  status = tt_int_error (category);
  return category;
}

Tt_status TTPattern::addAddress (Tt_address address)
{
  return tt_pattern_address_add (ttPattern, address);
}

Tt_status TTPattern::addClass (Tt_class tt_class)
{
  return tt_pattern_class_add (ttPattern, tt_class);
}

Tt_status TTPattern::addDisposition (Tt_disposition disposition)
{
  return tt_pattern_disposition_add (ttPattern, disposition);
}

Tt_status TTPattern::addFile (const char *file)
{
  return tt_pattern_file_add (ttPattern, file);
}

Tt_status TTPattern::addObject (const char *objid)
{
  return tt_pattern_object_add (ttPattern, objid);
}

Tt_status TTPattern::addObjectSpec (const TTObjectSpec &spec)
{
  Tt_status status;
  return tt_pattern_object_add (ttPattern, spec.getID (status));
}

Tt_status TTPattern::addOp (const char *op)
{
  return tt_pattern_op_add (ttPattern, op);
}

Tt_status TTPattern::addOType (const char *otype)
{
  return tt_pattern_otype_add (ttPattern, otype);
}

Tt_status TTPattern::addScope (Tt_scope scope)
{
  return tt_pattern_scope_add (ttPattern, scope);
}

Tt_status TTPattern::addSender (const char *sender)
{
  return tt_pattern_sender_add (ttPattern, sender);
}

Tt_status TTPattern::addSenderPType (const char *ptype)
{
  return tt_pattern_sender_ptype_add (ttPattern, ptype);
}

Tt_status TTPattern::addSession (const char *session)
{
  return tt_pattern_session_add (ttPattern, session);
}

Tt_status TTPattern::addState (Tt_state state)
{
  return tt_pattern_state_add (ttPattern, state);
}

Tt_status TTPattern::addArg (Tt_mode mode, const char *vtype, int value)
{
  return tt_pattern_iarg_add (ttPattern, mode, vtype, value);
}

Tt_status TTPattern::addArg (Tt_mode     mode,
			     const char *vtype,
			     const char *value)
{
  return tt_pattern_arg_add (ttPattern, mode, vtype, value);
}

Tt_status TTPattern::addArg (Tt_mode              mode,
			     const char          *vtype,
			     const unsigned char *value,
			     int                  length)
{
  return tt_pattern_barg_add (ttPattern, mode, vtype, value, length);
}

#if (TT_VERSION >= 10101)
Tt_status TTPattern::addArg (Tt_mode     mode,
                             const char *vtype,
                             xdrproc_t   xdr_proc,
                             void       *value)
{
  return tt_pattern_xarg_add (ttPattern, mode, vtype, xdr_proc, value);
}
#else
Tt_status TTPattern::addArg (Tt_mode, const char *, xdrproc_t, void *)
{
  return TT_ERR_UNIMP;
}
#endif   

#if (TT_VERSION >= 10100)
Tt_status TTPattern::addContext (const char *name, int value)
{
  return tt_pattern_icontext_add (ttPattern, name, value);
}
#else
Tt_status TTPattern::addContext (const char *, int)
{
  return TT_ERR_UNIMP;
}
#endif   

#if (TT_VERSION >= 10100)
Tt_status TTPattern::addContext (const char *name,
                                 const char *value)
{
  return tt_pattern_context_add (ttPattern, name, value);
}
#else
Tt_status TTPattern::addContext (const char *, const char *)
{
  return TT_ERR_UNIMP;
}
#endif   

#if (TT_VERSION >= 10100)
Tt_status TTPattern::addContext (const char          *name,
                                 const unsigned char *value,
                                 int                  length)
{
  return tt_pattern_bcontext_add (ttPattern, name, value, length);
}
#else
Tt_status TTPattern::addContext (const char *, const unsigned char *, int)
{
  return TT_ERR_UNIMP;
}
#endif
 
#if (TT_VERSION >= 10101)
Tt_status TTPattern::addContext (const char *name,
                                 xdrproc_t   xdr_proc,
                                 void       *value)
{
  return tt_pattern_xcontext_add (ttPattern, name, xdr_proc, value);
}
#else
Tt_status TTPattern::addContext (const char *, xdrproc_t, void *)
{
  return TT_ERR_UNIMP;
}
#endif
 
Tt_status TTPattern::setCallback (TTPatternCallback callback)
{
  Tt_status status = TT_OK;

  if (!patternCallback && !useDefaultPatternCallbackFlag)
    status = tt_pattern_callback_add (ttPattern,
				      &TTPattern::internalPatternCallback);

  patternCallback = callback;
  return status;
}

Tt_pattern TTPattern::getPattern () const
{
  return ttPattern;
}

void TTPattern::setPattern (Tt_pattern tt_pattern)
{
  if (ttPattern != tt_pattern) {
    decreaseReferenceCount ();

    ttPattern = tt_pattern;
    if (ttPattern) {
      Tt_status status = tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, this);
      checkNewPattern (status); 
    }
  }
}

void TTPattern::checkNewPattern (Tt_status status)
{
  if (status != TT_OK) {
    if (ttPattern) {
      (void)tt_pattern_destroy (ttPattern);
      ttPattern = (Tt_pattern)NULL;
    }
  }  
  else {
    initializeOrIncreaseReferenceCount ();
  }
}

void TTPattern::initializeOrIncreaseReferenceCount ()
{
  if (!ttPattern) return;

  int reference_count = (int)tt_pattern_user (ttPattern, TT_REFERENCE_COUNT);
  if ((tt_ptr_error ((void *)reference_count) != TT_OK) || !reference_count)
    (void)tt_pattern_user_set (ttPattern, TT_REFERENCE_COUNT, (void *)1);
  else
    (void)tt_pattern_user_set (ttPattern,
			       TT_REFERENCE_COUNT,
			       (void *) (reference_count + 1));
}

void TTPattern::increaseReferenceCount ()
{
  if (!ttPattern) return;

  int reference_count = (int)tt_pattern_user (ttPattern, TT_REFERENCE_COUNT);
  if ((tt_ptr_error ((void *)reference_count) == TT_OK) && reference_count)
    (void)tt_pattern_user_set (ttPattern,
			       TT_REFERENCE_COUNT,
			       (void *) (reference_count + 1));
}

void TTPattern::decreaseReferenceCount ()
{
  if (!ttPattern) return;

  int reference_count = (int)tt_pattern_user (ttPattern, TT_REFERENCE_COUNT);
  if (tt_ptr_error ((void *)reference_count) == TT_OK) {
    if (reference_count > 1) {
      (void)tt_pattern_user_set (ttPattern,
			         TT_REFERENCE_COUNT,
			         (void *) (reference_count - 1));

      if (this == tt_pattern_user (ttPattern, TT_OBJECT_KEY))
        (void)tt_pattern_user_set (ttPattern, TT_OBJECT_KEY, 0);
    }
    else {
      (void)tt_pattern_destroy (ttPattern);
    }
  }
}

Tt_status TTPattern::useDefaultPatternCallback (TTBoolean flag)
{
  Tt_status status = TT_OK;

  if (flag && !patternCallback && !useDefaultPatternCallbackFlag)
    status = tt_pattern_callback_add (ttPattern,
				      &TTPattern::internalPatternCallback);

  useDefaultPatternCallbackFlag = (status == TT_OK ? flag : TT_FALSE);
  return status;
}

Tt_callback_action TTPattern::defaultPatternCallback (TTMessage * /* message */)
{
  return TT_CALLBACK_CONTINUE;
}

Tt_callback_action TTPattern::internalPatternCallback (Tt_message message,
						       Tt_pattern pattern)
{
  TTPattern *pattern_object = (TTPattern *)tt_pattern_user (pattern,
							    TT_OBJECT_KEY);
  if (!pattern_object || (tt_ptr_error (pattern_object) != TT_OK))
    return TT_CALLBACK_CONTINUE;

  Tt_callback_action  action;
  TTMessage          *message_object;
  if (pattern_object->useDefaultPatternCallbackFlag) {
    message_object = new TTMessage (message);
    action = pattern_object->defaultPatternCallback (message_object);

    if (action != TT_CALLBACK_PROCESSED)
      message_object->setMessage ((Tt_message)NULL);
    delete message_object;
    return action;
  }
  else if (pattern_object->patternCallback) {
    message_object = new TTMessage (message);
    action = (*pattern_object->patternCallback) (pattern_object,
						 message_object);

    if (action != TT_CALLBACK_PROCESSED)
      message_object->setMessage ((Tt_message)NULL);
    delete message_object;
    return action;
  }

  return TT_CALLBACK_CONTINUE;
}
