/**
 * @file capism.h
 *
 * CapiStateMachine - Class declaration for a (very) simple CAPI state machine.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: capism.h,v 1.1.2.1 2006/02/28 21:35:39 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     capitest - Test tool for the functionality of CAPI for BSD $
 * @date        01.01.2000
 * @author      "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#ifndef __CAPISM_H
#define __CAPISM_H

#include <string>
#include <iostream>
#include <capi20.h>





// === public declarations ===============================================





// --- some constants ---

#define MAX_BDATA_LEN           2048
#define BDATA_MAX_SEND_WINDOW   8
#define BDATA_SEND_WINDOW       2





// === declaration of classes ============================================





// --- class for a simple CAPI state machine -----------------------------

class CCapiStateMachine
{
   public:
      // the state of the whole connection
      enum ConnectionState_t
      {
         CS_IDLE,               // idle state
         CS_ERROR_IDLE,         // connection in idle state, but error occurred
         CS_RUNNING,
         NUM_CONNECTION_STATES
      };
      
      // what to do with incoming data
      enum IncDataDest_t
      {
         IDD_IGNORE,
         IDD_ECHO,
         IDD_STORE_TO_FILE
      };
      
      // support types of connections
      enum ConnectionType_t
      {
         CT_AUTO,               // only for incoming calls: automatically
                                // determine the connection type from the CIP
                                // value
         CT_VOICE,              // voice connection, transparent B-channel
                                // protocols
         CT_DATA_HDLC,          // data connection, B1-protocol HDLC, others
                                // transparent
         CT_DATA_X75,           // data connection, all B-protocols on defaults
                                // (B1 HDLC, B2 X.75, B3 transparent)
         CT_FAXG3,              // fax-g3 connection; for incoming calls SFF is
                                // used as the data format, for outgoing calls
                                // ASCII text and SFF are supported and
                                // automatically detected
         NUM_CONNECTION_TYPES   // number of supported connection types
      };
      
      /// The possible states of the PLCI state machine.
      enum PlciState_t
      {
         PLCI_STATE_P0,         // idle state
         PLCI_STATE_P0_1,       // connect-req sent, wait for connect-conf
         PLCI_STATE_P1,         // connect-conf received, wait for
                                // connect-active-ind
         PLCI_STATE_P2,         // connect-ind received, decide wether to go to
                                // P4 or P5, must send connect-resp
         PLCI_STATE_P3,         // handset support, not implemented
         PLCI_STATE_P4,         // connect-resp sent, wait for
                                // connect-active-ind
         PLCI_STATE_PACT,       // active state, B-channel switched
         PLCI_STATE_P5,         // wait for disconnect-ind
         PLCI_STATE_P6,         // got disconnect-ind, next state is P0
         NUM_PLCI_STATES        // number of PLCI states
      };
      
      /// The possible states of the NCCI state machine.
      enum NcciState_t
      {
         NCCI_STATE_N0,         // idle state
         NCCI_STATE_N0_1,       // connect-b3-req sent, wait for
                                // connect-b3-conf to go to N2
         NCCI_STATE_N1,         // got connect-b3-ind, next state is N2
         NCCI_STATE_N2,         // wait for connect-b3-active-ind
         NCCI_STATE_NACT,       // active state, connection established
         NCCI_STATE_N3,         // resetting B3 connection, not implmented
         NCCI_STATE_N4,         // wait for disconnect-b3-ind
         NCCI_STATE_N5,         // got disconnect-b3-ind, next state is N0
         NUM_NCCI_STATES        // number of NCCI states
      };


      
      // constructor
      CCapiStateMachine
         (unsigned           uConnID,
          unsigned           uCapiApplID,
          unsigned           uController,
          const std::string &strCalledNumber,
          const std::string &strCallingNumber,
          ConnectionType_t   connType,
          bool               fDirectionIn,
          IncDataDest_t      incDataDest,
          const std::string &strDstFileName,
          const std::string &strSrcFileName);
          
      // no default constructor allowed, do only declaration
      CCapiStateMachine (void);
      
      // destructor
      ~CCapiStateMachine (void);
      
      /**
       * Set the flag for verbose output.
       */
      void SetVerboseOutput
         (bool f);

      /**
       * Set the flag to send out user-user data.
       */
      void SetSendUUData
         (bool f);

      // read the connection id of the state machine object
      unsigned GetConnectionID (void);
      
      // read the direction of the connection establishment
      bool GetDirectionIn (void);
      
      // read the current state of the connection
      ConnectionState_t GetConnectionState (void);
      
      /**
       * Get the connection type value for this connection.
       */
      ConnectionType_t GetConnectionType (void);
      
      /**
       * Get the controller this state machine works on.
       */
      unsigned GetController (void);
      
      /**
       * Get the currently active PLCI.
       *
       * If there is currently no PLCI active (e.g. right before the
       * Connect-Indication is delivered for an incoming call), the return
       * value will be null. After the connection is completed (i.e. after
       * receiving Disconnect-Indication), the formerly assigned PLCI will still
       * be returned by this function, the PLCI value will not be overwritten
       * until the object is destroyed.
       */
      u_int32_t GetPlci (void);
      
      /**
       * Get the current destination file name.
       */
      const std::string &GetDstFileName (void);
      
      // handler for incoming CAPI messages
      void HandleCapiMessage
         (const CAPIMsg_t *pCapiMsg);
         
      // start an outgoing connection
      unsigned DoConnectRequest (void);
      
      // terminate the connection
      unsigned TerminateConnection (void);
      
      /**
       * Query if the object is just waiting for a Connect-Confirm message.&
       */
      bool IsConnectRequestPending (void);
      
      /**
       * Get the message number for the last request message sent.
       */
      unsigned GetCurrentRequestMessageNumber (void);
      
      /**
       * Declare a connection object as failed.
       */
      void SetFailed (void);
      
      /**
       * Query the connection object for failure.
       */
      bool GetFailed (void);
   
   
   
   protected:
      /// The global continuous message numbering counter.
      static u_int16_t m_uhGlobalMessageCounter;
      
      /// Flag for verbose output during operation.
      bool m_fVerboseOutput;
      
      /// The connection number to identify each state machine object.
      unsigned m_uConnID;
      
      // application id and the controller for the connection
      unsigned m_uCapiApplID;
      unsigned m_uController;
      
      /// Direction of connection establishment.
      bool m_fDirectionIn;
      
      // at least for outgoing calls we need source and destination numbers
      std::string m_strCallingNumber;
      std::string m_strCalledNumber;
      
      // the B-channel protocols to use
      ConnectionType_t m_connType;
      unsigned         m_uB1Prot;
      unsigned         m_uB2Prot;
      unsigned         m_uB3Prot;
      
      // what to do with incoming data for incoming connections
      IncDataDest_t m_incDataDest;
      std::string   m_strDstFileName;
      int           m_fdDstFile;
      
      // what to do for outgoing data for outgoing connections (always read
      // from file)
      std::string m_strSrcFileName;
      int         m_fdSrcFile;
      bool        m_fSrcFaxG3Sff;
      unsigned    m_uSrcFaxG3Resolution;
      
      /// Overall state of the complete state machine.
      ConnectionState_t m_connState;
      
      /// Flag if the connection was in the active state, i.e.
      /// Connect-B3-Active-Indication was received.
      bool m_fWasActive;
      
      /// Flag if no error occurred during the connection.
      bool m_fConnOk;
      
      // the state for the PLCI state machine
      PlciState_t m_plciState;
      u_int32_t   m_dwPlci;
      bool        m_fActiveDisconnect;
      
      // the state for the NCCI state machine
      NcciState_t m_ncciState;
      u_int32_t   m_dwNcci;
      bool        m_fActiveDisconnectB3;
      
      // two buffers for sending data blocks are needed (send window of two
      // unconfirmed data blocks)
      unsigned char m_aaucDataBuf [BDATA_MAX_SEND_WINDOW] [MAX_BDATA_LEN];
      int           m_iCurrBlockIdx;
      unsigned      m_uCurrDataHandle;
      unsigned      m_uConfirmedDataHandle;
      
      /// The message number for the last / current pending request message.
      unsigned m_uCurrReqMsgNum;
      
      /// The message number for a pending Connect-Indication to be answered
      /// after an Alert-Confirm.
      unsigned m_uConnIndMsgNum;

      
      
      // --- Data for verifying integrity of the transmitted data stream ---
      
      /// Flag to transmit the connection type in a Connect-Request and the
      /// transmitted number of bytes with a checksum in an Info-Indicatin at
      /// the end of the connection to the partner.
      bool m_fSendUUData;
      
      /// Byte counter for incoming data to verify data integrity.
      size_t m_nNumBytesIncoming;
      
      /// Checksum for incoming data to verify data integrity.
      unsigned short m_uhChecksumIncoming;
      
      /// Byte counter for outgoing data to send to the other side as user-user
      /// data.
      size_t m_nNumBytesOutgoing;
      
      /// Checksum for outgoing data to send to the other side as user-user
      /// data.
      unsigned short m_uhChecksumOutgoing;
      
      /// Total number of bytes signaled by the other side.
      /// If this value is zero, no length or checksum information (member
      /// m_uhChecksumFromPartner) was signaled and no data integrity check is
      /// possible.
      size_t m_nNumBytesFromPartner;
      
      /// Checksum signaled by the other side.
      unsigned short m_uhChecksumFromPartner;
   
   
   
      // --- handle incoming CAPI messages ---
      
      /**
       * Handle acknowledge of an alerting message for a pending
       * Connect-Indication.
       */
      void HandleAlertConf
         (const CAPIMsg_t *pCapiMsg);

      // handle new incoming connection
      void HandleConnectInd
         (const CAPIMsg_t *pCapiMsg);
      
      // handle acknowledge for new outgoing connection
      void HandleConnectConf
         (const CAPIMsg_t *pCapiMsg);

      // handle signalling of activated PLCI connection
      void HandleConnectActInd
         (const CAPIMsg_t *pCapiMsg);

      // handle terminated connection
      void HandleDisconnectInd
         (const CAPIMsg_t *pCapiMsg);

      // handle acknowledge for terminating the current connection
      void HandleDisconnectConf
         (const CAPIMsg_t *pCapiMsg);

      // handle several network indications
      void HandleInfoInd
         (const CAPIMsg_t *pCapiMsg);

      /**
       * Handle a confirmation for an Info-Request.
       */
      void HandleInfoConf
	 (const CAPIMsg_t *pCapiMsg);

      // handle indicated start of B3 connection establishment
      void HandleConnectB3Ind
         (const CAPIMsg_t *pCapiMsg);

      // handle acknowledge for start of outgoing B3 connection
      void HandleConnectB3Conf
         (const CAPIMsg_t *pCapiMsg);

      // handle activated B3 connection
      void HandleConnectB3ActInd
         (const CAPIMsg_t *pCapiMsg);

      // handle terminated B3 connection
      void HandleDisconnectB3Ind
         (const CAPIMsg_t *pCapiMsg);

      // handle acknowledge for terminating the current B3 connection
      void HandleDisconnectB3Conf
         (const CAPIMsg_t *pCapiMsg);

      // handle incoming data block
      void HandleDataB3Ind
         (const CAPIMsg_t *pCapiMsg);

      // handle acknowledge for data block to send
      void HandleDataB3Conf
         (const CAPIMsg_t *pCapiMsg);

      // handle an unexpected CAPI message
      void HandleUnexpectedMessage
         (const CAPIMsg_t *pCapiMsg);



      // --- send out CAPI messages ---
      
      /**
       * Start an outgoing connection.
       */
      unsigned SendConnectReq (void);
      
      /**
       * Send an Alert-Request for a pending Connect-Indication.
       */
      unsigned SendAlertReq (void);
      
      /**
       * Answer an incoming call.
       */
      unsigned SendConnectResp
         (unsigned  uMsgNum,
          u_int16_t wReject);
      
      // terminate the current connection
      unsigned SendDisconnectReq (void);
      
      // terminate the current B3 connection
      unsigned SendDisconnectB3Req (void);
      
      /**
       * Send an Info-Request for checksum information if so configured.
       */
      unsigned SendChecksumUUData (void);
      
      // send the next data block for an outgoing connection
      unsigned SendNextDataBlock (void);
      
      /**
       * Send a received data block back to the originator.
       */
      void EchoDataBlock
         (u_int8_t *pabData,
          size_t    nLenData);

      // send a dummy response for an unhandled CAPI message
      unsigned SendDummyResponse
         (const CAPIMsg_t *pCapiIndMsg);
      


      // --- Evaluate or create some parts of CAPI messages ---
      
      // enter the BProtocol structure into a CAPI message
      unsigned char *EnterBProtocol
         (unsigned char *pOrgPos);

      /**
       * Create an additional info structure with user-user data for the
       * connection type.
       */
      u_int8_t *EnterAddInfoForConnTypeUUData
         (u_int8_t *pbOrgPos);

      /**
       * Create an additional info structure with user-user data for checksum
       * information.
       */
      u_int8_t *EnterAddInfoForChecksumUUData
         (u_int8_t *pbOrgPos);

      /**
       * Evaluate user-user data in an additional info structure.
       *
       * @note We assume the CAPI message and especially the additional info
       *       structure is correctly coded. Otherwise we would need to check
       *       several times if the end of the whole CAPI message or of some
       *       structures is reached. But for a (relatively) simple test program
       *       we skip these checks.
       */
      u_int8_t *EvalUUDataFromAddInfo
         (u_int8_t *pbAddInfo);

      /**
       * Evaluate a user-user data information element.
       */
      u_int8_t *EvalUUDataElement
         (u_int8_t *pbUUData);



      // --- Miscellaneous ---
      
      /**
       * Get a file extension for a connection type value.
       */
      const char *GetFileExtFromConnType
         (ConnectionType_t connType);

      /**
       * Update a checksum value according to a data block.
       */
      void UpdateChecksum
         (unsigned short &uhChecksum,
          u_int8_t       *pabData,
          size_t          nLenData);



   private:
   
   
   
}; // CCapiStateMachine





/*
        read the connection id of the state machine object
        --------------------------------------------------
*/

unsigned inline CCapiStateMachine::GetConnectionID (void)
{
   return (m_uConnID);
} // CCapiStateMachine::GetConnectionID





/*
        read the direction of the connection establishment
        --------------------------------------------------
*/

bool inline CCapiStateMachine::GetDirectionIn (void)
{
   return (m_fDirectionIn);
} // CCapiStateMachine::GetDirectionIn





/*
        read the current state of the connection
        ----------------------------------------
*/

CCapiStateMachine::ConnectionState_t inline
   CCapiStateMachine::GetConnectionState (void)
{
   return (m_connState);
} // CCapiStateMachine::GetConnectionState
      




// === prototypes of interface functions =================================





/**
 * Operator to print out a connection state value as a string.
 */
std::ostream & operator<<
   (std::ostream                         &os,
    CCapiStateMachine::ConnectionState_t  connState);

/**
 * Operator to print out the destination of incoming data as a string.
 */
std::ostream & operator<<
   (std::ostream                     &os,
    CCapiStateMachine::IncDataDest_t  incDataDst);

/**
 * Operator to print out a connection type value as a string.
 */
std::ostream & operator<<
   (std::ostream                        &os,
    CCapiStateMachine::ConnectionType_t  connType);

/**
 * Operator to print out a PLCI state value as a string.
 */
std::ostream & operator<<
   (std::ostream                   &os,
    CCapiStateMachine::PlciState_t  plciState);

/**
 * Operator to print out a NCCI state value as a string.
 */
std::ostream & operator<<
   (std::ostream                   &os,
    CCapiStateMachine::NcciState_t  ncciState);





#endif // __CAPISM_H
