// Copyright (c) 2006-2009 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:
//



// INCLUDE FILES
#include "cmmlinetsy.h"
#include "cmmphonetsy.h"
#include "cmmcalltsy.h"
#include "cmmlinelist.h"
#include "cmmcalllist.h"
#include "cmmtsyreqhandlestore.h"
#include "MmTsy_numberOfSlots.h"
#include <ctsy/tflogger.h>
#include <ctsy/pluginapi/cmmdatapackage.h>
#include <et_struct.h>


// ======== MEMBER FUNCTIONS ========

CMmLineTsy::CMmLineTsy()
    {
    }

void CMmLineTsy::ConstructL()
    {
    TFLOGSTRING("TSY: CMmLineTsy::ConstructL");
    //Initialise miscellaneous internal attributes
    InitInternalAttributesL();

    // Create and store a Call Object for incoming calls.
    TInt ret = CreateCallObjectForIncomingCall();
    
    if ( KErrNone != ret )
        {
        //if this fails, incoming calls cannot be called -> leave
        User::Leave( KErrNoMemory );
        }

    //create req handle store
    iTsyReqHandleStore = CMmTsyReqHandleStore::NewL( 
        EMultimodeLineMaxNumOfRequests, iLineReqHandles );
    }

CMmLineTsy::~CMmLineTsy()
    {
    TFLOGSTRING2("TSY: CMmLineTsy::~CMmLineTsy. Line name: %S", &iLineName);

    //delete req handle store
    delete iTsyReqHandleStore;

    //delete incoming call object
    if ( iCallForIncomingCall )
        {
        iCallForIncomingCall->Close();
        }

    // Remove all calls still open from this line
    iMmPhone->CallList()->RemoveCallsByLine( &iLineName );

    //Inform phone that this line has been removed.
    iMmPhone->RemoveLine( iLineName );

    iMmPhone = NULL;
    iCallForIncomingCall = NULL;
    iRetLineStatus = NULL;
    iRetCaps = NULL;
    iRetIncomingCallName = NULL;
    iRetCallAdded = NULL;
    iRetMobileLineStatus = NULL;
    iTsyReqHandleStore = NULL;
    iMessageManager = NULL;
    }


// ---------------------------------------------------------------------------
// CMmLineTsy::OpenNewObjectByNameL
// Returns a pointer to an existing call. The TSY classes must not use this 
// method. This method is called when the client uses RCall::OpenExistingCall 
// method.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
CTelObject* CMmLineTsy::OpenNewObjectByNameL(
    const TDesC& aName )
    {
    TFLOGSTRING2("TSY: CMmLineTsy::OpenNewObjectByNameL %S", &aName);

    TName mmCallName( aName );
    CMmCallTsy* mmCall = iMmPhone->CallList()->GetMmCallByName( &mmCallName );

    //if not found, Leave...
    if ( NULL == mmCall )
        {
        User::Leave( KErrNotFound );
        }
    else
        {
        //Update iUnownedCallObject flag
        mmCall->SetUnownedCallObjectFlag( EFalse );
        }

    return mmCall;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::InitInternalAttributesL
// Initialises miscellaneous internal attributes.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::InitInternalAttributesL()
    {
    // Set object attributes
    iLineStatus = RCall::EStatusIdle;    
    iMobileLineStatus = RMobileCall::EStatusIdle;

    //reset the name for answering call
    iNameOfCallForAnswering.Zero();

    iNumCalls = 0;    // Number of calls created from line;
    iCallSequenceNumber = 0;    // The sequence number for the calls
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CallObjectForIncomingCall
// This method returns a Call object that is used for a new incoming call. 
// If there are no incoming calls this method creates a new call object.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
CMmCallTsy* CMmLineTsy::CallObjectForIncomingCall()
    {
    CMmCallTsy* callForIncomingCall = NULL;

    if ( iCallForIncomingCall == NULL )
        {
        CreateCallObjectForIncomingCall();
        }

    callForIncomingCall = iCallForIncomingCall;

    return callForIncomingCall;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::ReqModeL
// ReqModeL is called from the server's CTelObject::ReqAnalyserL in order to 
// check the type of request it has
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
CTelObject::TReqMode CMmLineTsy::ReqModeL(
    const TInt aIpc )
    {
    TFLOGSTRING2("TSY: CMmLineTsy::ReqModeL IPC:%d",aIpc);
    
    CTelObject::TReqMode ret( 0 );    // default return value
    
    switch( aIpc )
        {
        // Non-flow control requests
        // All disabled
        // TSYs wishing to implement their own buffering algorithm will place
        // all requests in this category. This category will also be used by
        // the RecoverDataPort request that must be passed to the TSY to
        // indicate that it may use the communications port after a loan.
                
        //Get methods that do not use DOS and return immediately.
        //Flow control not required.
        case EEtelLineGetStatus:
        case EEtelLineGetCaps:
        case EEtelLineGetCallInfo:
        case EEtelLineGetInfo:
        case EEtelLineGetHookStatus:
        case EMobileLineGetMobileLineStatus:

        //Other methods that do not use DOS and return immediately.
        //Flow control not required.
        case EEtelLineEnumerateCall:

        //Methods that can propably take a long time and cannot therefore be
        //flow controlled. Solution: All these methods must check req handle
        //table before handling the request. In case that the request table
        //indicates that same method has been called and has not been
        //completed, the method should return KErrServerBusy.
            break;
        // Flow Controlled Services
        // KReqModeFlowControlObeyed
        // Commands that change the state of the phone, e.g. clearing the AoC
        // counter; are commands that the TSY should only deal with one at
        // a time.
        
        //    ret = KReqModeFlowControlObeyed;
        //    break;

        // ReqModePostImmediately
        // Requests that notify a client about a change of state, where the
        // TSY needs to distinguish between different clients.
            //ret=KReqModeRePostImmediately;
            //break;

        // KReqModeMultipleCompletionEnabled
        // (a) commands that may take some time, but which the TSY can handle
        // more than one of concurrently, or 
        // (b) notifications that the TSY does not wish to be re-posted
        // immediately, so the server does no buffering.
            //ret=KReqModeMultipleCompletionEnabled;
            //break;

        // KReqModeMultipleCompletionEnabled | KReqModeFlowControlObeyed
        // A command that may take some time and which the TSY can only deal 
        // with one at a time.
            //ret = KReqModeMultipleCompletionEnabled |
            //    KReqModeFlowControlObeyed;
            //break;

        // Notification Requests
        // KReqModeMultipleCompletionEnabled | ReqModePostImmediately
        // Requests that notify a client about a change of state.
        // Since these requests do not require the issuing of any modem
        // commands, they do not have to obey flow control. The TSY never
        // gets more than one of these outstanding per CTelObject.
        case EEtelLineNotifyStatusChange:
        case EEtelLineNotifyCallAdded:
        case EETelLineCapsChangeNotification:
        case EEtelLineNotifyHookChange:
        case EEtelLineNotifyIncomingCall:
        case EMobileLineNotifyMobileLineStatusChange:
            ret = KReqModeMultipleCompletionEnabled |
                KReqModeRePostImmediately;
            break;
        // Cancel Requests
        //It is not necessary to include the Cancel methods in ReqModeL at all
        //The ETel server never calls ReqModeL with a Cancel IPC.

        //Other variations of return values are unusable

        // Default: Call CLineBase's ReqModeL.
        default:
            ret = CLineBase::ReqModeL( aIpc );
            break;
        }

    return ret;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::ExtFunc
// TRAP's all CMmLineTsy related MM API requests in cases that they fail. This 
// method functions only as a centralized TRAP for the DoExtFuncL method that 
// does the actual mapping of IPC number to TSY method call.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::ExtFunc(
    const TTsyReqHandle aTsyReqHandle,
    const TInt aIpc,
    const TDataPackage& aPackage )
    {
    TInt ret( KErrNone );
    TInt trapError( KErrNone );

    //reset last tsy request type
    iReqHandleType = EMultimodeLineReqHandleUnknown;

    //Original code continues here.
    TRAP( trapError, ret = DoExtFuncL( aTsyReqHandle, aIpc, aPackage ); );

    if ( trapError != KErrNone )
        {
        // Object cannot be created.
        ReqCompleted( aTsyReqHandle, trapError );
        }
    else if ( ret != KErrNone )
        {
        ReqCompleted( aTsyReqHandle, ret );
        }
    
    //save request handle
    if ( EMultimodeLineReqHandleUnknown != iReqHandleType )
        {
        iTsyReqHandleStore->SetTsyReqHandle( iReqHandleType, aTsyReqHandle );
        }

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::DoExtFuncL
// ExtFunc is called by the server when it has a "extended", i.e. non-core  
// ETel request for the TSY. To process a request handle, request type and 
// request  data are passed to the TSY. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::DoExtFuncL(
    const TTsyReqHandle aTsyReqHandle,
    const TInt aIpc,
    const TDataPackage& aPackage )
    {
    TFLOGSTRING3("TSY: CMmLineTsy::DoExtFuncL IPC:%d Handle:%d", aIpc, \
        aTsyReqHandle);

    TInt ret ( KErrNone );

    TAny* dataPtr = aPackage.Ptr1();

    // The request data has to extracted from TDataPackage and the TAny* 
    // pointers have to be "cast" to the expected request data type
    switch ( aIpc )
        {
      // Mobile Line Status
        // Get Mobile Line Status
        case EMobileLineGetMobileLineStatus:
            ret = GetMobileLineStatus( aTsyReqHandle,
            REINTERPRET_CAST( RMobileCall::TMobileCallStatus*, dataPtr ) );
            break;
        //Notify Change of Mobile Line Status
        case EMobileLineNotifyMobileLineStatusChange:
            ret = NotifyMobileLineStatusChange(
            REINTERPRET_CAST( RMobileCall::TMobileCallStatus*, dataPtr ) );
            break;
        default:
            ret = KErrNotSupported;
            break;
        }

    return ret;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CreateNewCallName
// Creates a new name for the call, which is the name of the line followed by 
// a call sequence number.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::CreateNewCallName(
    TDes& aNewName )
    {
    // buffer for the name
    TBuf<KMaxName> buf;
    // append line name first
    buf.Append( iLineName );
    // append call sequence number next
    buf.AppendNum( IncrementCallSequenceNumber() );
    // copy the created name
    aNewName.Copy( buf );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::IncrementCallSequenceNumber
// Returns the sequence number of the call created from this line. This 
// sequence number will be resetted when this line object is deleted. TInt 
// allows the value to increment to the value 2^32. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TUint CMmLineTsy::IncrementCallSequenceNumber()
    {
    // increment call sequence number
    iCallSequenceNumber++;
    return iCallSequenceNumber;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::EnumerateCall
// This CORE API method returns the number of calls opened from a line. The 
// number of calls will be stored in the aCount pointer.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::EnumerateCall(
    const TTsyReqHandle aTsyReqHandle,
    TInt* aCount )
    {
    *aCount = iNumCalls;
    ReqCompleted( aTsyReqHandle, KErrNone );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::DecrementNumberOfCalls
// Decrements number of calls opened from a line by one.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::DecrementNumberOfCalls()
    {
    if ( iNumCalls > 0 )
        {
        iNumCalls--;
        }
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyStatusChange
// This CORE API method provides notification about a change in the line 
// status
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyStatusChange(
    const TTsyReqHandle aTsyReqHandle,
    RCall::TStatus* aLineStatus )
    {
    iRetLineStatus = aLineStatus;
    iTsyReqHandleStore->SetTsyReqHandle( EMultimodeLineNotifyStatusChange,
        aTsyReqHandle );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyStatusChangeCancel
// This CORE API method cancels an outstanding line status change notification 
// request, placed using the NotifyStatusChange() method.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyStatusChangeCancel(
    const TTsyReqHandle aTsyReqHandle )
    {
    iRetLineStatus = NULL;
    iTsyReqHandleStore->ResetTsyReqHandle( EMultimodeLineNotifyStatusChange );
    ReqCompleted( aTsyReqHandle, KErrCancel );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CompleteNotifyStatusChange
// This method is used to notify to the client about the line status change. 
// This method should only be called by the CMmCallTsy's 
// CompleteNotifyStatusChange method to guaratee that this is functioning 
// right. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::CompleteNotifyStatusChange()
    {
    TInt ret( KErrNone );
    TBool coreStatusChanged( EFalse );
    TBool mobileStatusChanged( EFalse );
    RCall::TStatus tempCoreStatus( RCall::EStatusIdle );
    RMobileCall::TMobileCallStatus tempMobileStatus( 
        RMobileCall::EStatusIdle );
    TInt numberOfObjectsInCallList = iMmPhone->CallList()->
        GetNumberOfObjects();

    for ( TInt i = 0; i < numberOfObjectsInCallList; i++ )
        {
        CMmCallTsy* mmCall = iMmPhone->CallList()->GetMmCallByIndex(i);
        
        //check that the call object has been opened from this line
        if ( mmCall->Line() == this )
            {
            RMobileCall::TMobileCallStatus mobileStatus 
                = mmCall->MobileCallStatus();
            switch( mobileStatus )
                {
                case RMobileCall::EStatusUnknown:
                    tempCoreStatus = RCall::EStatusUnknown;
                    tempMobileStatus = RMobileCall::EStatusUnknown;
                    //get out of loop, resulting status is unknown...
                    i = numberOfObjectsInCallList;
                    break;
                case RMobileCall::EStatusConnected:
                    tempCoreStatus = RCall::EStatusConnected;
                    tempMobileStatus = RMobileCall::EStatusConnected;
                    //get out of loop, resulting status is unknown...
                    i = numberOfObjectsInCallList;
                    break;
                case RMobileCall::EStatusRinging:
                    //update core status
                    if ( tempCoreStatus == RCall::EStatusIdle )
                        {
                        tempCoreStatus = RCall::EStatusRinging;
                        }
                    //update mobile status
                    if ( tempMobileStatus == RMobileCall::EStatusIdle || 
                         tempMobileStatus == RMobileCall::EStatusHold)
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusDialling:
                    if ( tempCoreStatus == RCall::EStatusIdle )
                        {
                        tempCoreStatus = RCall::EStatusDialling;
                        }
                    //update mobile status
                    if ( tempMobileStatus == RMobileCall::EStatusIdle || 
                         tempMobileStatus == RMobileCall::EStatusHold)
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusAnswering:
                    if ( tempCoreStatus == RCall::EStatusIdle ||
                         tempCoreStatus == RCall::EStatusRinging )
                        {
                        tempCoreStatus = RCall::EStatusAnswering;
                        }
                    //update mobile status
                    if ( tempMobileStatus == RMobileCall::EStatusIdle || 
                         tempMobileStatus == RMobileCall::EStatusRinging ||
                         tempMobileStatus == RMobileCall::EStatusHold )
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusConnecting:
                    if ( tempCoreStatus == RCall::EStatusIdle || 
                            tempCoreStatus == RCall::EStatusDialling )
                        {
                        tempCoreStatus = RCall::EStatusConnecting;
                        }
                    //update mobile status
                    if ( tempMobileStatus == RMobileCall::EStatusIdle ||
                         tempMobileStatus == RMobileCall::EStatusDialling ||
                         tempMobileStatus == RMobileCall::EStatusHold )
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusDisconnecting:
                    //update core status
                    if ( tempCoreStatus == RCall::EStatusIdle )
                        {
                        tempCoreStatus = RCall::EStatusHangingUp;
                        }
                    if ( tempMobileStatus == RMobileCall::EStatusIdle )
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusWaitingAlternatingCallSwitch:
                    if ( tempMobileStatus == RMobileCall::EStatusIdle )
                        {
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusHold:
                    if ( tempMobileStatus == RMobileCall::EStatusIdle || 
                         tempMobileStatus == 
                            RMobileCall::EStatusDisconnecting )
                        {
                        tempCoreStatus = iLineStatus;
                        tempMobileStatus = mobileStatus;
                        }
                    break;
                case RMobileCall::EStatusIdle:
                case RMobileCall::EStatusReconnectPending:
                case RMobileCall::EStatusDisconnectingWithInband:
                case RMobileCall::EStatusTransferring:
                case RMobileCall::EStatusTransferAlerting:
                default:
                    break;
                }
            }
        }

    //check if core status has changed
    if ( tempCoreStatus != iLineStatus )
        {
        coreStatusChanged = ETrue;
        iLineStatus = tempCoreStatus;
        }

    //check if mobile status has changed
    if ( tempMobileStatus != iMobileLineStatus )
        {
        mobileStatusChanged = ETrue;
        iMobileLineStatus = tempMobileStatus;
        }

    //get core status change notification req handle
    TTsyReqHandle iNotifyStatusChangeHandle = iTsyReqHandleStore->
                GetTsyReqHandle( EMultimodeLineNotifyStatusChange );

    //if req handle available and status changed
    if ( ( iNotifyStatusChangeHandle > 0 ) && coreStatusChanged )
        {
        *iRetLineStatus = iLineStatus; 
        iTsyReqHandleStore->ResetTsyReqHandle(
            EMultimodeLineNotifyStatusChange );
        ReqCompleted( iNotifyStatusChangeHandle, ret );
        }

    //get mobile status change notification req handle
    TTsyReqHandle iNotifyMobileLineStatusChangeHandle = iTsyReqHandleStore->
        GetTsyReqHandle( EMultimodeLineMobileLineStatusChange );

    //if req handle available and status changed
    if ( iNotifyMobileLineStatusChangeHandle > 0 
        && ( coreStatusChanged || mobileStatusChanged ) )
        {
        *iRetMobileLineStatus = iMobileLineStatus; 
        iTsyReqHandleStore->ResetTsyReqHandle(
            EMultimodeLineMobileLineStatusChange );
        ReqCompleted( iNotifyMobileLineStatusChangeHandle, ret );
        }
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetHookStatus
// This CORE API method retrieves the current hook status. Method is not 
// currently supported,
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetHookStatus(
    const TTsyReqHandle aTsyReqHandle,
    RCall::THookStatus* )
    {
    ReqCompleted( aTsyReqHandle, KErrNotSupported );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetInfo
// This CORE API method retrieves the current line information.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetInfo(
    const TTsyReqHandle aTsyReqHandle,
    RLine::TLineInfo* aLineInfo )
    {
    // The current hook status.
    aLineInfo->iHookStatus = RCall::EHookStatusUnknown; 
    // The current line status.
    aLineInfo->iStatus = iLineStatus;
    // The name of the last call created on the line.
    aLineInfo->iNameOfLastCallAdded = iNameOfLastCallAdded;
    // The name of the call to which a new incoming call will be directed.
    aLineInfo->iNameOfCallForAnswering = iNameOfCallForAnswering;
 
    ReqCompleted( aTsyReqHandle, KErrNone );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::LineInfo
// This method retrieves the current line information. The method is used by 
// MmPhone object.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
RPhone::TLineInfo CMmLineTsy::LineInfo() const
    {        
    // Line info
    RPhone::TLineInfo lineInfo;
    // The current line status.
    lineInfo.iStatus = iLineStatus;
    // line capabilities
    lineInfo.iLineCapsFlags = iLineCaps.iFlags;
    // line name
    lineInfo.iName = iLineName;

    return lineInfo;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyCapsChange
// This CORE API method provides notification of a change in the line 
// capabilities.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyCapsChange(
    const TTsyReqHandle aTsyReqHandle,
    RLine::TCaps* aCaps )
    {
    // On return, contains the new line capabilities
    iRetCaps = aCaps;
    iTsyReqHandleStore->SetTsyReqHandle( EMultimodeLineCapsChangeNotification,
        aTsyReqHandle );
    
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyCapsChangeCancel
// This CORE API method cancels an "line capabilities change" notification 
// request, placed using the NotifyCapsChange() method.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyCapsChangeCancel(
    const TTsyReqHandle aTsyReqHandle )
    {
    iRetCaps = NULL;
    iTsyReqHandleStore->ResetTsyReqHandle(
        EMultimodeLineCapsChangeNotification );
    ReqCompleted( aTsyReqHandle, KErrCancel );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CompleteNotifyCapsChange
// This method is used to notify to the client about the capabilities change.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::CompleteNotifyCapsChange()
    {
    //reset req handle. Returns the deleted req handle
    TTsyReqHandle reqHandle = iTsyReqHandleStore->ResetTsyReqHandle( 
        EMultimodeLineCapsChangeNotification );

    if ( EMultimodeLineReqHandleUnknown != reqHandle )
        {
        *iRetCaps = iLineCaps; 
        ReqCompleted( reqHandle, KErrNone );
        }
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyIncomingCall
// This CORE API method is used to notify a client when an incoming call is 
// detected.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyIncomingCall(
    const TTsyReqHandle aTsyReqHandle,
    TName* aName )
    {
    // On notification, contains the name of the incoming call.
    iRetIncomingCallName = aName;    
    iTsyReqHandleStore->SetTsyReqHandle( EMultimodeLineNotifyIncomingCall,
         aTsyReqHandle );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyIncomingCallCancel
// This CORE API method cancels an outstanding incoming call notification, 
// placed with the NotifyIncomingCall() method.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyIncomingCallCancel(
    const TTsyReqHandle aTsyReqHandle )
    {
    iRetIncomingCallName = NULL;
    iTsyReqHandleStore->ResetTsyReqHandle( EMultimodeLineNotifyIncomingCall );
    ReqCompleted( aTsyReqHandle, KErrCancel );
    return KErrNone;    
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyCallAdded
// This CORE API method provides notification that a new call has been added 
// to the line.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyCallAdded(
    const TTsyReqHandle aTsyReqHandle,
    TName* aName )
    {
    TFLOGSTRING2("TSY: CMmLineTsy::NotifyCallAdded requested by client, \
        lineMode:%d", iLineMode);
    // On return, contains the name of the new call.
    iRetCallAdded = aName;    
    iTsyReqHandleStore->SetTsyReqHandle( EMultimodeLineNotifyCallAdded,
        aTsyReqHandle );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyCallAddedCancel
// This CORE API method cancels an outstanding "new call added" notification 
// request, placed using the NotifyCallAdded() method.(other items were 
// commented in a header).
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyCallAddedCancel(
    const TTsyReqHandle aTsyReqHandle )
    {
    TFLOGSTRING("TSY: CMmLineTsy::NotifyCallAddedCancel requested by client");
    iRetCallAdded = NULL;
    iTsyReqHandleStore->ResetTsyReqHandle( EMultimodeLineNotifyCallAdded );
    ReqCompleted( aTsyReqHandle, KErrCancel );
    return KErrNone;    
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CompleteNotifyCallAdded
// This CORE API method is used to notify to the client that a call has been 
// added to the line. NOTE: Type is not defined because this method may be 
// called when incoming call occurs or when a call object has been created. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::CompleteNotifyCallAdded(const TDesC& aName )    
    {
    TFLOGSTRING3("TSY: CMmLineTsy::CompleteNotifyCallAdded entered, \
    CALL ADDED, Call name: %S, Call mode: %d", &aName, iLineMode );

    //reset req handle. Returns the deleted req handle
    TTsyReqHandle reqHandle = iTsyReqHandleStore->ResetTsyReqHandle( 
                                EMultimodeLineNotifyCallAdded );

    iNumCalls++;

    if ( EMultimodeLineReqHandleUnknown != reqHandle )
        {
        TFLOGSTRING("TSY: CMmLineTsy::CompleteNotifyCallAdded, Completed!");
        *iRetCallAdded = aName;
        ReqCompleted( reqHandle, KErrNone );
        }
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetCaps
// This CORE API method retrieves the line capabilities.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetCaps(
    const TTsyReqHandle aTsyReqHandle,
    RLine::TCaps* aCaps )
    {
    *aCaps = iLineCaps;
    ReqCompleted( aTsyReqHandle, KErrNone );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetStatus
// This CORE API method returns core line status information.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetStatus(
    const TTsyReqHandle aTsyReqHandle,
    RCall::TStatus* aStatus )
    {    
    *aStatus = iLineStatus;
    ReqCompleted( aTsyReqHandle, KErrNone );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyHookChange
// This CORE API method provides notification when the hook status changes. 
// Feature is not supported.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyHookChange(
    const TTsyReqHandle aTsyReqHandle,
    RCall::THookStatus* )
    {    
    ReqCompleted( aTsyReqHandle, KErrNotSupported );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyHookChangeCancel
// This CORE API method cancels an outstanding hook change notification req
// placed using the NotifyHookChange() method. Feature is not supported.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyHookChangeCancel(
    const TTsyReqHandle aTsyReqHandle )
    {    
    ReqCompleted( aTsyReqHandle, KErrNotSupported );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetCallInfo
// This CORE API method returns information about a call. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetCallInfo(
    const TTsyReqHandle aTsyReqHandle,
    TCallInfoIndex* aCallInfoIndex )
    {
    TFLOGSTRING3("TSY: CMmLineTsy::GetCallInfo - Line name: %S, Index: %d",
        &iLineName, aCallInfoIndex->iIndex );

    TInt ret( KErrNotFound );

    // Check if the call object can be found from call list by index
    CMmCallTsy* mmCall = REINTERPRET_CAST( CMmCallTsy*, 
            iMmPhone->CallList()->GetMmCallByIndexAndLine( 
                aCallInfoIndex->iIndex, &iLineName ) );

    // If call object was found, fill the client pointer with call information
    if ( mmCall )
        {
        aCallInfoIndex->iInfo.iCallName      = mmCall->CallName();
        aCallInfoIndex->iInfo.iStatus        = mmCall->Status();
        aCallInfoIndex->iInfo.iCallCapsFlags = mmCall->CallCaps();

        TFLOGSTRING3("TSY: CMmLineTsy::GetCallInfo - Call name: %S, Status: %d",
            &aCallInfoIndex->iInfo.iCallName, aCallInfoIndex->iInfo.iStatus );

        ret = KErrNone;
        }

    ReqCompleted( aTsyReqHandle, ret );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CancelService
// CancelService is called by the server when it is "cleaning-up" any still 
// outstanding asynchronous requests before closing a client's sub-session. 
// This will happen if a client closes its R-class handle without cancelling 
// outstanding asynchronous requests. Core API requests are directed to the 
// LineBase. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::CancelService(
    const TInt aIpc, 
    const TTsyReqHandle aTsyReqHandle )
    {
    TInt ret( KErrNone );
    
    // When the clients close their sub-sessions (eg. by calling RLine::Close)
    // they may not have cancelled all their outstanding asynchronous requests 
    // before closing.  It is up to the ETel server to clean up in this 
    // situation, so the server will find the list of outstanding requests 
    // related to that sub-session object and pass these outstanding IPC 
    // request numbers, one at a time, to the CancelService method in the TSY.

    switch ( aIpc )
        {
        //TSY has started a request and it is not possible to then cancel
        //this request. The best thing for the TSY to do in this case is to
        //proceed as though the Cancel never happened. The server's call to
        //the TSY cancel function will return synchronously. The TSY then
        //continues to wait for the original acknowledgement and when it
        //receives it, the TSY will complete the original request.
        //case XXX
        //    break;

        //Cancel methods that are not supported
        //case XXX:
        //    ret = KErrNotSupported;
        //    break;

        //Notification Cancels, no special requirements.
        case EMobileLineNotifyMobileLineStatusChange:
            ret =  NotifyMobileLineStatusChangeCancel( aTsyReqHandle );
            break;
        //Everything is taken care in the method implementation.
        //Just direct the request to the method.
        //case XXX:
        //    ret = XXXCancel( aTsyReqHandle );
        //    break;

        default:
            return CLineBase::CancelService( aIpc, aTsyReqHandle );
        }

    return ret;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::RegisterNotification
// RegisterNotification is called when the server recognises that this 
// notification is being posted for the first time on this sub-session object. 
// It enables the TSY to "turn on" any regular notification messages that it 
// may receive from DOS. Currently does not really do anything but returns  
// KErrNone to ETel server in case of known notification request type. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::RegisterNotification(
    const TInt aIpc )
    {    
    TInt ret( KErrNone );

    switch ( aIpc )
        {
        case EEtelLineNotifyStatusChange:
        case EEtelLineNotifyCallAdded:
        case EETelLineCapsChangeNotification:
        case EEtelLineNotifyHookChange:
        case EEtelLineNotifyIncomingCall:
        case EMobileLineNotifyMobileLineStatusChange:
            ret = KErrNone;
            break;
        default:
            // Unknown or invalid IPC
            ret = KErrNotSupported;
        }
    return ret;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::DeregisterNotification
// DeregisterNotification is called when the server recognises that this 
// notification will not be posted again because the last client to have a 
// handle on this sub-session object has just closed the handle. It enables 
// TSY to "turn off" any regular notification messages that it may receive 
// from DOS. Currently does not really do anything but returns KErrNone to  
// ETel server in case of known notification request type. 
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::DeregisterNotification(
    const TInt aIpc )
    {
    TInt ret( KErrNone );

    switch ( aIpc )
        {
        case EEtelLineNotifyStatusChange:
        case EEtelLineNotifyCallAdded:
        case EETelLineCapsChangeNotification:
        case EEtelLineNotifyHookChange:
        case EEtelLineNotifyIncomingCall:
        case EMobileLineNotifyMobileLineStatusChange:
            ret = KErrNone;
            break;
        default:
            // Unknown or invalid IPC
            ret = KErrNotSupported;
        }
    return ret;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NumberOfSlotsL
// NumberOfSlotsL is called by the server when it is registering a new 
// NOTIFICATION. It enables the TSY to tell the server how many buffer slots 
// to allocate for "repost immediately" notifications that may trigger before 
// clients collect them.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NumberOfSlotsL(
    const TInt aIpc )
    {    
    //There is a buffer in the ETel server for each type of NOTIFICATION and
    //the size of the buffer is determined by the TSY. When the ETel server
    //discovers that a request is "repost immediately" it will ask the TSY how
    //big a buffer it wants by calling CTelObject::NumberOfSlotsL( ).
    //
    //It is up to the TSY developer's judgement how many buffer slots this
    //method returns for each notification. If there is a danger that a
    //particular notification could trigger frequently and in rapid succession
    //(e.g. call group change notification) then at least 10 or 20 slots may
    //be required. For a notification that triggers rarely, perhaps 1 or 2
    //slots is enough.
    //
    //So if the ETel server has stored a few occurrences of a particular
    //notification, when a client subsequently calls that notification request
    //the ETel server will complete the client's request with the saved data.
    
    TInt numberOfSlots( 1 );

    switch ( aIpc )
        {
        case EEtelLineNotifyStatusChange:
            numberOfSlots = KMmLineStatusChangeSlots;
            break;
        case EEtelLineNotifyCallAdded:
            numberOfSlots = KMmLineCallAddedSlots;
            break;
        case EETelLineCapsChangeNotification:
            numberOfSlots = KMmLineCapsChangeSlots;
            break;
        case EEtelLineNotifyHookChange:
            numberOfSlots = KMmLineHookChangeSlots;
            break;
        case EEtelLineNotifyIncomingCall:
            numberOfSlots = KMmLineIncomingCallSlots;
            break;
        case EMobileLineNotifyMobileLineStatusChange:
            numberOfSlots = KMmLineMobileLineStatusChangeSlots;
            break;
        default:  
            // Unknown or invalid Line IPC
            User::Leave( KErrNotSupported );
            break;
        }
    return numberOfSlots;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::LineMode
// Returns line mode.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
RMobilePhone::TMobileService CMmLineTsy::LineMode() const
    {
    return iLineMode;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::LineName
// Returns line name.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TName CMmLineTsy::LineName() const
    {
    return iLineName;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::GetMobileLineStatus
// Returns the current status of the line through the aStatus argument. The 
// possible line states map to the call states (see MM API spec) so that a 
// line that has one or more calls opened from it will have a status defined 
// by this table.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::GetMobileLineStatus(
    const TTsyReqHandle aTsyReqHandle,
    RMobileCall::TMobileCallStatus* aStatus )
    {
    *aStatus = iMobileLineStatus;
    ReqCompleted( aTsyReqHandle, KErrNone );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyMobileLineStatusChange
// Allows a client to be notified when the mobile line changes state. The 
// request completes when the line changes state, the new state being copied 
// to the aStatus parameter.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyMobileLineStatusChange(
    RMobileCall::TMobileCallStatus* aStatus )
    {
    iRetMobileLineStatus = aStatus;
    iReqHandleType = EMultimodeLineMobileLineStatusChange;
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::NotifyMobileLineStatusChangeCancel
// Cancels an outstanding asynchronous NotifyMobileLineStatusChange request.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
TInt CMmLineTsy::NotifyMobileLineStatusChangeCancel(
    const TTsyReqHandle aTsyReqHandle )
    {
    iRetMobileLineStatus = NULL;
    iTsyReqHandleStore->ResetTsyReqHandle(
        EMultimodeLineMobileLineStatusChange );
    ReqCompleted( aTsyReqHandle, KErrCancel );
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CMmLineTsy::CompleteNotifyAddBypassingCall
// Handles ghost call object creation and initialisation. Also notifies 
// clients about the ghost call (using NotifyCallAdded method)
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::CompleteNotifyAddBypassingCall(
    CMmDataPackage* aDataPackage )
    {
    TInt callId( -1 );
    RMobilePhone::TMobileService callMode( RMobilePhone::EVoiceService );
    RMobileCall::TMobileCallInfoV1* mobileCallInfo;

    const CCallDataPackage* callData = 
        reinterpret_cast<const CCallDataPackage*>(aDataPackage);

    callData->GetCallIdAndMode( callId, callMode );
    callData->UnPackData ( &mobileCallInfo );

    if ( mobileCallInfo->iStatus == RMobileCall::EStatusDialling ||
         mobileCallInfo->iStatus == RMobileCall::EStatusConnecting ||
         mobileCallInfo->iStatus == RMobileCall::EStatusConnected )
        {
        TFLOGSTRING2("TSY: CMmLineTsy::CompleteNotifyAddBypassingCall, \
            CALL INITIATED - NOT REQUESTED BY ETEL's CLIENT, Call ID: %d",
            callId );

        //create new call object
        CMmCallTsy* mmGhostCall = CreateGhostCallObject(
            callId, callMode, mobileCallInfo->iStatus );

        if ( mmGhostCall )
            {
            // Add the new call to the list of calls
            TInt ret( iMmPhone->CallList()->AddObject( mmGhostCall ) );

            if ( KErrNone == ret )
                {
                mmGhostCall->CompleteNotifyMobileCallInfoChange( 
                    aDataPackage );
                CompleteNotifyStatusChange();
                //inform ETel client that someone is creating a MO call
                CompleteNotifyCallAdded( mmGhostCall->CallName() );
                mmGhostCall->SetGhostCall( ETrue );
                }
            else
                {
                delete mmGhostCall;
                }
            }
        }
    else
        {
        TFLOGSTRING3("TSY: CMmLineTsy::CompleteNotifyAddBypassingCall, \
            CALL NOT INITIATED - Call ID:%d, Call status:%d", 
            callId, mobileCallInfo->iStatus);
        }
    }

#ifdef TF_LOGGING_ENABLED
// ---------------------------------------------------------------------------
// CMmLineTsy::ReqCompleted
// Overloads CTelObject::ReqCompleted for logging purposes. It prints the 
// aTsyReqHandle and aError variable in the log file and then calls 
// CTelObject::ReqCompleted.
// (other items were commented in a header).
// ---------------------------------------------------------------------------
//
void CMmLineTsy::ReqCompleted(
    const TTsyReqHandle aTsyReqHandle,
    const TInt aError )
    {
    TFLOGSTRING3("TSY: CMmLineTsy::ReqCompleted Handle:%d Error:%d", 
        aTsyReqHandle, aError);

    CTelObject::ReqCompleted(aTsyReqHandle,aError);        
    }

#endif


//  End of File 
