/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
*
*/


#ifndef CSIPCONNECTION_H
#define CSIPCONNECTION_H

#include "musunittesting.h"
//  INCLUDES
#include <e32base.h>
#include <in_sock.h>

// FORWARD DECLARATIONS
class CSIP;
class MSIPConnectionObserver;
class CSIPTransactionBase;
class CSIPClientTransaction;
class CSIPServerTransaction;
class MSIPRegistrationContext;
class CSIPRegistrationBinding;
class CRegistrationState;
class CSIPDialog;
class CDialogState;
class CSIPRefresh;
class CSIPMessageElements;
class CSIPRequestElements;
class CSIPResponseElements;
class CSIPClientConnectionObserver;
class CConnectionCallback;
class CSIPClientConnection;


//CONSTANTS
/** SIP minimum value for the expiration in seconds*/
//const TUint KSIPMinExpirationValue=60;


// CLASS DECLARATION

/**
*  Class for monitoring connection's state and sending SIP requests outside
*  the dialog associations.Class must not be used
*  -for sending requests to which received non-failure response creates a dialog
*  -for sending requests that must be sent within SIP dialog
* Note that the user can have only one instance of the class per same IAP-id
*/
class CSIPConnection :
    public CBase
	{
    public:
    
        /** Connection states */
        enum TState 
            {
            /** Connection initializing */
			EInit=1,
			/** Connection active */
			EActive,
			/** Connection suspended */
			ESuspended,
			/** Connection is inactive */
			EInactive,
			/** Connection is permanently unavailable */
			EUnavailable
            };

    public:  // Constructors and destructor

	    /**
        * Two-phased constructor.
		* @param aSip a handle to the SIP server
		* @param aIapId IAP-id
		* @param aObserver a observer for receiving asynchonous events on this
        *   connection
        * @return New object, ownership is transferred
        */

		IMPORT_C static CSIPConnection*
            NewL(CSIP& aSIP,
				 TUint32 aIapId,
				 MSIPConnectionObserver& aObserver);


	    /**
        * Two-phased constructor.
		* Constructs an object and adds the pointer to the cleanup stack;
		* @param aSip a handle to the SIP server
		* @param aIapId IAP-id
		* @param aObserver a observer for receiving asynchonous events on this
        *   connection
        * @return New object, ownership is transferred
        */

		IMPORT_C static CSIPConnection*
            NewLC(CSIP& aSIP,
				  TUint32 aIapId,
				  MSIPConnectionObserver& aObserver);


		/**
		* Destructor
		*/

		IMPORT_C ~CSIPConnection();

    public: // New functions

        /**
		* Gets current state of the connection        
        * @return current state of the connection
        */

		IMPORT_C TState State() const;

        /**
		* Creates SIP request and sends it to the given destination.
		* The function must not be used with SIP requests that creates
		* SIP dialog associations or must be sent within SIP dialog
		* association.
		* This provokes the Non-invite state machine.
		* This function must be used if the user has created the
		* registration binding.
		* @pre State() == EActive.
		* @pre aContext.IsContextActive()==ETrue
		* @param aElements contains local and remote addresses,
		*		 optional SIP message headers and body.
		*        The ownership is transferred.
		* @param aContext defines outbound proxy to be used. If the user
		*        does not provide from-address it will be constucted using
		*        user's AOR used during the registration.
        * @return SIP client transaction, ownership is transferred
        */

		IMPORT_C CSIPClientTransaction*
            SendRequestL(CSIPRequestElements* aElements,
                         const MSIPRegistrationContext& aContext);


        /**
		* Creates SIP request and sends it to the given destination.
		* The function must not be used with SIP request that creates
		* SIP dialog associations or must be sent within SIP diallog
		* association.
		* This provokes the Non-invite state machine.
		* This function must be used if the user has created the
		* registration binding.
		* @pre State() == EActive.
		* @pre aContext.IsContextActive()==ETrue
		* @param aElements contains local and remote addresses,
		*		 optional SIP message headers and body.
		*        The ownership is transferred.
		* @param aContext defines outbound proxy to be used. If the user
		*        does not provide from-address it will 
		*        be constucted using user's AOR used during the registration.
		* @param aRefresh defines the refresh interval for this transaction.
        * @return SIP client transaction, ownership is transferred
        */

		IMPORT_C CSIPClientTransaction*
            SendRequestL(CSIPRequestElements* aElements,                                        
                         const MSIPRegistrationContext& aContext,
						 CSIPRefresh& aRefresh); 


        /**
		* Creates SIP request and sends it to the given destination.
		* The function must not be used with SIP request that creates SIP
		* diallog associations or must be sent within SIP dialog association.
		* This invokes the Non-invite state machine.
		* @pre State() == EActive.
		* @pre aElements.FromHeader() != 0
		* @param aElements contains local and remote addresses,
		*		 optional SIP message headers and body.
		*        The ownership is transferred.
        * @return SIP client transaction, ownership is transferred
        */

		IMPORT_C CSIPClientTransaction*
            SendRequestL(CSIPRequestElements* aElements);
        

        /**
		* Creates SIP request and sends it to the given destination.
		* The function must not be used with SIP request that creates SIP
		* dialog associations or must be sent within SIP dialog association.
		* This invokes the Non-invite state machine.
		* @pre State() == EActive.
		* @pre aElements.FromHeader() != 0
		* @param aElements contains local and remote addresses,
		*		 optional SIP message headers and body.
		*        The ownership is transferred.
		* @param aRefresh indicates that transaction will be refreshed
        * @return SIP client transaction, ownership is transferred
        */

		IMPORT_C CSIPClientTransaction*
            SendRequestL(CSIPRequestElements* aElements,
						 CSIPRefresh& aRefresh);


        /**
		* Creates SIP REGISTER request and sends it to the given destination.
		* User must no define any Contact or Expires-headers
		* in the provided elements.
		* @pre State() == EActive
        * @pre aElements != 0
		* @pre aElements->Method().DesC().Length()==0
		* @param aElements contains local and remote addresses, optional SIP
        *        message headers and body. User must not set any expires values
        *        to aElements. The ownership is transferred.
        * @return SIP REGISTER client transaction, ownership is transferred
        */

		IMPORT_C CSIPClientTransaction*
            FetchRegistrationsL(CSIPRequestElements* aElements);
            
        // Not in real SIP API:
        IMPORT_C void SetState(TState aState);    

		//TODO:SHOULD THIS NEW FUNCTION BE INCLUDED INTO STUB???
		/**
		* Gets Iap-id used for this connection
		* @return Iap-id
		*/
		
		IMPORT_C TUint32 IapId() const;

	        /**
        * Gets the local IP address of this CSIPConnection 
        * that is used by the SIP implementation.
        * The user should re-new the call whenever the connection state changes
        * to the CSIPConnection::EActive in order to verify the correctness of
        * the previously retrieved local IP address. The local IP address might
        * be different as a result of the connection state change.
        * @pre State() == CSIPConnection::EActive
        * @param aAddr on return filled with local IP address
        * @leave a system-wide error code
        */ 	                         
	    IMPORT_C void GetLocalAddrL(TInetAddr& aAddr) const;
		
		
		/**
        * Gets SIP server handle
        * @return handle to SIP server, or 0-pointer if SIP server does not
        *	exist any more
        */
        IMPORT_C CSIP* SIP();
  
		/**
        * Gets SIP server handle
        * @return handle to SIP server, or 0-pointer if SIP server does not
        *	exist any more
        */
        
        IMPORT_C const CSIP* SIP() const;
		//TODO:ENDS
		
        /**
		* Incoming request outside dialog has been received.
		*
		* @param aTransaction Server transaction. Ownership is transferred.        
        */

        void IncomingRequestL(CSIPServerTransaction* aTransaction) const;

        
        /**
		* Incoming request within dialog has been received.
		*
		* @param aTransaction Server transaction. Ownership is transferred.
        * @param aDialogId Identifies the dialog to which the transaction
        *   belongs to.
        */

        void IncomingDialogRequestL(CSIPServerTransaction* aTransaction,
                              		TUint32 aDialogId);

        void IncomingResponseL(CSIPResponseElements* aElements,
                               TUint32 aRequestId) const;
    
        void IncomingRegistrationResponseL(CSIPResponseElements* aElements,
		                       			   TUint32 aRequestId,
					           			   TUint32 aRegistrationId) const;

        void IncomingRefreshResponseL(CSIPResponseElements* aElements,
		                       		  TUint32 aRequestId,
							   		  TUint32 aRefreshId);


        void ErrorOccured(TInt aError, TUint32 aRequestId) const;

        void RefreshErrorOccured(TInt aError, TUint32 aRefreshId) const;

	    void RegistrationErrorOccured(TInt aError,
                          			  TUint32 aRegistrationId,
                          			  TUint32 aRequestId) const;

		void DialogErrorOccured(TInt aError,
                          		TUint32 aDialogId,
                          		TUint32 aRequestId) const;

        void DialogErrorOccured(TInt aError,
                          		TUint32 aDialogId,
                          		TUint32 aRefreshId,
                          		TUint32 aRequestId) const;

        void ConnectionStateChanged(CSIPConnection::TState aState);

        void InviteCompleted(TUint32 aRequestId) const;


		/**
        * Searches for a transaction with the matching aRequestId.
		* @param aRequestId RequestId associated with the transaction
		* @return Found transaction or NULL if not found. Ownership is not
        *   transferred.
        */
        
        CSIPTransactionBase* FindTransaction(TUint32 aRequestId) const;
   
   
        TInt AddRegistration(CSIPRegistrationBinding* aRegistration);
        void RemoveRegistration(const CSIPRegistrationBinding* aRegistration);
        

      /**
        * Searches for a registration binding, using aRegistrationId and
        * aRequestId as keys. Registration might not yet have received
        * RegistrationId from SIP client, so the aRequestId may be needed to
        * locate the registration binding.
        * @param aRegistrationId RegistrationId of the registration binding
		* @param aRequestId RequestId of a transaction associated with the
        *   registration
		* @return Found registration binding or NULL if not found.
		*	Ownership is not transferred.
        */
        
        CSIPRegistrationBinding* FindRegistration(TUint32 aRegistrationId,
                             			   		  TUint32 aRequestId) const;


        TInt AddDialog(CSIPDialog* aDialog);
        void RemoveDialog(const CSIPDialog* aDialog);


        /**
        * Searches for a dialog, using aRequestId as a key.
        * @param aRequestId RequestId of a transaction associated with the
        *   dialog
		* @return Found dialog or NULL if not found. Ownership is not
        *   transferred.
        */

        CSIPDialog* FindDialogByRequestId(TUint32 aRequestId) const;


        /**
		* Informs CSIPConnection that the associated CSIP instance has been
        * deleted. After this the CSIPConnection object can't be used anymore.
		*/

        void CSIPDeleted();


        /**
		* Obtains the initial state for registration state machine.
        *
        * @return Initial registration state, ownership is not transferred
		*/

        CRegistrationState* InitialRegisterStateL() const;


        /**
		* Obtains the initial state for dialog state machine.
        *
        * @return Initial dialog state, ownership is not transferred
		*/

        CDialogState* InitialDialogStateL() const;


        /**
        * Response related to a dialog has been received.
		*
        * @param aElements SIP response elements. Ownership is transferred.
        * @param aRequestId Identifies the transaction
        * @param aDialogId DialogId
        * @param aRefreshId NULL if the response is not related to a refresh,
        *   otherwise the RefreshId. Ownership is not transferred.
		*/

        void IncomingResponseToDialogL(CSIPResponseElements* aElements,
		                               TUint32 aRequestId,
                                       TUint32 aDialogId,
                                       TUint32* aRefreshId);

    private:
        CSIPConnection(CSIP& aSIP,
                       TUint32 aIapId,
                       MSIPConnectionObserver& aObserver);

        void ConstructL();


        /**
        * Send a SIP request.
        *
		* @param aElements SIP headers. Ownership is transferred.
        * @param aRegistrationId If non-NULL, request will use the outbound
        *   proxy and some SIP headers from the registration identified by
        *   aRegistrationId. Ownership is not transferred.
        * @param aRefresh If non-NULL, the request will be refreshed. Otherwise
        *   NULL. Ownership is not transferred.
		* @return New transaction. Ownership is transferred.
        */

        CSIPClientTransaction*
            SendRequestL(CSIPRequestElements* aElements,
                         TUint32* aRegistrationId,
						 CSIPRefresh* aRefresh);


        /**
        * Searches for a refresh and transaction, using aRequestId and
        * aRefreshId as keys.        
		* @param aRequestId RequestId associated with the refreshed transaction
        * @param aRefreshId RefreshId associated with the refresh instance
        * @param aRefresh OUT: if refresh was found, this pointer is set to the
        *   refresh instance. Ownership is not transferred.
        * @param aTransaction OUT: if transaction was found, this pointer is
        *   set to the refreshed client transaction. Ownership is not
        *   transferred.
		* @return ETrue if found, EFalse otherwise
        */

        TBool FindRefresh(TUint32 aRequestId,
                          TUint32 aRefreshId,
                          CSIPRefresh** aRefresh,
                          CSIPClientTransaction** aTransaction) const;

        /**
        * Searches for a registration binding, using aRequestId as a key.
		* @param aRequestId RequestId of a transaction associated with the
        *   registration binding
		* @return Found registration binding or NULL if not found.
		*	Ownership is not transferred.
        */

        CSIPRegistrationBinding* FindRegistration(TUint32 aRequestId) const;

        
        /**
        * Searches for a dialog, using aDialogId as a key.        
        * @param aDialogId DialogId of the dialog.
		* @return Found dialog or NULL if not found. Ownership is not
        *   transferred.
        */

        CSIPDialog* FindDialog(TUint32 aDialogId) const;


        /**
        * A request has been received with a dialogId, but no matching dialog
        * exists.
		* If the request is NOTIFY, it is stored until a response with the same
        * dialogId is received. Then the response is passed to dialog and after
        * that all the stored NOTIFY are passed to the same dialog.
        * This is needed since NOTIFY can be received before a sent SUBSCRIBE
        * receives a response with dialogId.
		*
        * @param aTransaction Server transaction, ownership is transferred
        * @param aDialogId DialogId
		*/

        void IncomingRequestBeforeDialogExistsL(
                                    CSIPServerTransaction* aTransaction,
                                    TUint32 aDialogId);


        /**
		* Extracts the oldest stored transaction for the dialog identified by
        * aDialogId.
        * @param aDialogId DialogId
        * @return Server transaction or NULL if no transaction is stored for
        *   the dialog. Ownership is transferred.
		*/

        CSIPServerTransaction* GetStoredTransaction(TUint32 aDialogId);


        /**
		* Checks that CSIP is available for use (not NULL). If iSIP is NULL,
        * it means user has deleted a resource needed by CSIPConnection, and
        * this function leaves.
		*/

        void CheckCSipL() const;       


        /**
		* Connection is no longer available. All registrations, dialogs,
        * transactions and refreshes using this connection are terminated, but
        * not deleted.
		*/

        void ConnectionLost();


        //The used CSIP instance. CSIPConnection doesn't own the CSIP.
        //If this pointer is NULL; it means application has deleted the
        //CSIP instance before deleting the CSIPConnection, and this
        //CSIPConnection is now useless.
        CSIP* iSIP;

        //IAP-id of this connection
        TUint32 iIapId;

        //Callback to upper layer
        MSIPConnectionObserver& iObserver;

        //Callback receiving events from client
        CSIPClientConnectionObserver* iClientConnectionObserver;        

        //Connection used by this CSIPConnection instance
        CSIPClientConnection* iClientConnection;

        //CSIPConnection doesn't own CSIPRegistrationBindings
        RPointerArray<CSIPRegistrationBinding> iRegistrations;

        //CSIPConnection doesn't delete the CSIPDialog objects. CSIPDialog
        //deletes itself when its last dialog association is deleted.
        //NOTE: When CSIPDialog has just been created and has no dialog
        //associations yet, it must be stored in CleanupStack, otherwise it
        //won't be freed if leave occurs!
        RPointerArray<CSIPDialog> iDialogs;

        //CSIPConnection has to know which transactions are attached to it, but
        //it doesn't own the transactions. Transaction's ownership is passed
        //to application.
        RPointerArray<CSIPTransactionBase> iTransactions;

        //Standalone refreshes, application owns these
        RPointerArray<CSIPRefresh> iRefreshes;

        //To avoid using callbacks to MSIPConnectionObserver from within
        //CSIPRegistrationBinding and CSIPDialog, CConnectionCallback is passed to
        //the methods which would need to use MSIPConnectionObserver calls.
        //CSIPRegistrationBinding and CSIPDialog will fill CConnectionCallback with
        //the selected callback method and its parameters, and by returning
        //ETrue they tell to CSIPConnection that a callback has to be done.
        CConnectionCallback* iCallbackInfo;
        
        
        // Not in real SIP API
        TState iState;

        __DECLARE_TEST;

#ifdef CPPUNIT_TEST	
	    friend class CSIP_Test;
        friend class CSIPConnection_Test;
        friend class CSIPSubscribeDialogAssoc_Test;
        friend class CSIPInviteDialogAssoc_Test;
        friend class CSIPReferDialogAssoc_Test;
        friend class CSIPDialogTrying_Test;
        friend class CSIPRegistrationBinding_Test;
#endif
	};

#endif

