//
// Copyright (C) 2004 SIPfoundry Inc.
// License by SIPfoundry under the LGPL license.
// 
// Copyright (C) 2004 Pingtel Corp.
// Licensed to SIPfoundry under a Contributor Agreement.
//
//////////////////////////////////////////////////////////////////////////////

// SYSTEM INCLUDES
#include "assert.h"

// APPLICATION INCLUDES
#include "utl/UtlString.h"
#include "CallStateEventBuilder.h"
#include "os/OsSysLog.h"

// DEFINES
// MACROS
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS

enum BuildStates // used in builderStateOk
{
   Initial          = 0x0000, 
   BetweenEvents    = 0x0001,
   CallEventStarted = 0x0002, /* --\                                                 */
   CallDataAdded    = 0x0004, /*  these three are ORed together to form combinations */
   ViaAdded         = 0x0008  /* --/                                                 */
};

// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS

/* //////////////////////////// PUBLIC //////////////////////////////////// */

/* ============================ CREATORS ================================== */

/// Instantiate an event builder and set the observer name for its events
CallStateEventBuilder::CallStateEventBuilder(const char* observerDnsName ///< the DNS name to be recorded in all events
                                             ) :
   buildState(Initial)
{
   assert(NULL!=observerDnsName);

   observerName = strdup(observerDnsName);
}


/// Event validity checking state machine
/**
 * Each of the public builder methods calls this finite state machine to determine
 * whether or not the call is valid.  This allows all builders to share the same
 * rules for what calls are allowed and required when constructing each event type
 * (for this reason, this should not be redefined by derived classes).
 *
 * In the initial state, the only valid call is builderStateOk(ObserverStart)
 *
 * @returns
 *   - true if method is valid now
 *   - false if method is not valid now
 *
 * This state machine is defined by the valid call state events specified
 * in the meta/call_event.xsd schema.
 */
bool CallStateEventBuilder::builderStateIsOk( BuilderMethod method )
{
   bool isValid = false; // pessimism is safe
#  define VALID(newState) { isValid = true; buildState = (newState); }
#  define INVALID /* no-op, because isValid is already false, but useful as documentation */
   
   if (BuilderReset == method)
   {
      // always valid - equivalent to a builderReset call
      VALID(BetweenEvents);
   }
   else
   {
      switch (buildState)
      {
      case Initial:
         // only BuilderStart is valid, which was handled above
         INVALID;
         break;

      case BetweenEvents:
         switch (method)
         {
         case CallRequestEvent:
         case CallSetupEvent:
         case CallFailureEvent:
         case CallEndEvent:
         case CallTransferEvent:
            VALID(CallEventStarted);
            break;
         default:
            INVALID;
         }
      
      case CallEventStarted:
      case CallEventStarted|ViaAdded:
         switch (method)
         {
         case AddCallData:
            VALID(buildState|CallDataAdded);
            break;
         case AddVia:
            VALID(buildState|ViaAdded);
            break;
         default:
            INVALID;
         }
      
      case CallEventStarted|CallDataAdded:
         switch (method)
         {
         case AddVia:
            VALID(buildState|ViaAdded);
            break;
         default:
            INVALID;
         }

      case CallEventStarted|CallDataAdded|ViaAdded:
         switch (method)
         {
         case AddVia:
            VALID(buildState);
            break;
         case CompleteCallEvent:
            VALID(BetweenEvents);
            break;
         default:
            INVALID;
         }
         
      default:
      {
         INVALID;
      }
      }
   }

   return isValid;
}

/// Destructor
CallStateEventBuilder::~CallStateEventBuilder()
{
   assert(NULL!=observerName);

   free(const_cast<char*>(observerName));
}


/**
 * Generate a metadata event.
 * This method generates a complete event - it does not require that the callEventComplete method be called.
 */
void CallStateEventBuilder::observerEvent(int sequenceNumber, ///< for ObserverReset, this should be zero
                                          const OsTime& timestamp,      ///< obtain using getCurTime(OsTime)
                                          ObserverEvent eventCode,
                                          const char* eventMsg ///< for human consumption
                                          )
{
   bool CalledBaseClassMethod_observerEvent = false;
   assert(CalledBaseClassMethod_observerEvent);
}


/// Begin a Call Request Event - an INVITE without a to tag has been observed
/**
 * Requires:
 *   - callRequestEvent
 *   - addCallData (the toTag in the addCallRequest will be a null string)
 *   - addEventVia (at least for via index zero)
 *   - completeCallEvent
 */
void CallStateEventBuilder::callRequestEvent(int sequenceNumber,
                                             const OsTime& timestamp,      ///< obtain using getCurTime(OsTime)
                                             const UtlString& contact
                                             )
{
   bool CalledBaseClassMethod_callRequestEvent = false;
   assert(CalledBaseClassMethod_callRequestEvent);
}


/// Begin a Call Setup Event - a 2xx response to an INVITE has been observed
/**
 * Requires:
 *   - callSetupEvent
 *   - addCallData
 *   - addEventVia (at least for via index zero)
 *   - completeCallEvent
 */
void CallStateEventBuilder::callSetupEvent(int sequenceNumber,
                                           const OsTime& timestamp,      ///< obtain using getCurTime(OsTime)
                                           const UtlString& contact
                                           )
{
   bool CalledBaseClassMethod_callSetupEvent = false;
   assert(CalledBaseClassMethod_callSetupEvent);
}


/// Begin a Call Failure Event - an error response to an INVITE has been observed
/**
 * Requires:
 *   - callFailureEvent
 *   - addCallData
 *   - addEventVia (at least for via index zero)
 *   - completeCallEvent
 */
void CallStateEventBuilder::callFailureEvent(int sequenceNumber,
                                             const OsTime& timestamp,      ///< obtain using getCurTime(OsTime)
                                             int statusCode,
                                             const UtlString& statusMsg
                                             )
{
   bool CalledBaseClassMethod_callFailureEvent = false;
   assert(CalledBaseClassMethod_callFailureEvent);
}


/// Begin a Call End Event - a BYE request has been observed
/**
 * Requires:
 *   - callEndEvent
 *   - addCallData
 *   - addEventVia (at least for via index zero)
 *   - completeCallEvent
 */
void CallStateEventBuilder::callEndEvent(const int sequenceNumber,
                                         const OsTime& timestamp      ///< obtain using getCurTime(OsTime)
                                         )
{
   bool CalledBaseClassMethod_callEndEvent = false;
   assert(CalledBaseClassMethod_callEndEvent);
}


/// Begin a Call Transfer Event - a REFER request has been observed
/**
 * Requires:
 *   - callEndEvent
 *   - addCallData
 *   - addEventVia (at least for via index zero)
 *   - completeCallEvent
 */
void CallStateEventBuilder::callTransferEvent(int sequenceNumber, 
                                              const OsTime& timeStamp, 
                                              const UtlString& contact,
                                              const UtlString& refer_to,
                                              const UtlString& referred_by,
                                              const UtlString& request_uri)
{
   bool CalledBaseClassMethod_callTransferEvent = false;
   assert(CalledBaseClassMethod_callTransferEvent);    
}


/// Add the dialog and call information for the event being built.
void CallStateEventBuilder::addCallData(const int cseqNumber,
                                        const UtlString& callId,
                                        const UtlString& fromTag,  /// may be a null string
                                        const UtlString& toTag,    /// may be a null string
                                        const UtlString& fromField,
                                        const UtlString& toField
                                        )
{
   bool CalledBaseClassMethod_addCallData = false;
   assert(CalledBaseClassMethod_addCallData);
}

   
/// Add a via element for the event
/**
 * Record a Via from the message for this event 
 * Calls to this routine are in reverse cronological order - the last
 * call for an event should be the via added by the message originator
 */
void CallStateEventBuilder::addEventVia(const UtlString& via
                                        )
{
   bool CalledBaseClassMethod_addEventVia = false;
   assert(CalledBaseClassMethod_addEventVia);
}


/// Indicates that all information for the current call event has been added.
void CallStateEventBuilder::completeCallEvent()
{
   bool CalledBaseClassMethod_completeCallEvent = false;
   assert(CalledBaseClassMethod_completeCallEvent);
}



syntax highlighted by Code2HTML, v. 0.9.1