// smsevent.cpp
// 
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//

#include "smsevent.h"
#include "sms.h"

_LIT(KInternationlDialDirectIndicator,	"+");

//
// CSmsEventBase
//
CSmsEventBase::CSmsEventBase(CCmdSms& aObserver):
CActive(CActive::EPriorityStandard), iObserver(aObserver)
	{
	CActiveScheduler::Add(this);
	}

//
// CSmsEventDial
//
CSmsEventDial* CSmsEventDial::NewL(CCmdSms& aObserver, RCall::TTelNumberC& aTelNumber)
	{
	CSmsEventDial* self = new (ELeave) CSmsEventDial(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL(aTelNumber);
	CleanupStack::Pop(self);
	return self;
	}

CSmsEventDial::CSmsEventDial(CCmdSms& aObserver):
CSmsEventBase(aObserver), iState(ESmsEventNetworkIdle)
	{
	}

CSmsEventDial::~CSmsEventDial()
	{
	Cancel();
	iTimeout.Cancel();
	iTimeout.Close();
	if (iCall.SubSessionHandle() > 0)
		iCall.Close(); // should also force a hangup on the call object if it's active
	if (iLine.SubSessionHandle() > 0)
		iLine.Close();
	if (iPhone.SubSessionHandle() > 0)
		iPhone.Close();
	if (iTelServer.Handle() > 0)
		iTelServer.Close();
	}

void CSmsEventDial::ConstructL(RCall::TTelNumberC& aTelNumber)
	{
	// timeout timer 
	iTimeout.CreateLocal();
	
	// capture the number to dial
	iNumber.iTypeOfNumber = RMobilePhone::ENationalNumber;
	iNumber.iNumberPlan = RMobilePhone::EIsdnNumberPlan;
	iNumber.iTelNumber.Copy(aTelNumber);
	
	// check if there's a '+' at the front of the number
	if (aTelNumber.MatchC(KInternationlDialDirectIndicator) == 0)
		iNumber.iTypeOfNumber = RMobilePhone::EInternationalNumber;
	
	// connect to ETel. Note we're assuming the TSY has been loaded already
	RTelServer::TPhoneInfo phoneInfo;
	User::LeaveIfError(iTelServer.Connect());
	User::LeaveIfError(iTelServer.GetPhoneInfo(0, phoneInfo));
	
	// open the first phone object
	User::LeaveIfError(iPhone.Open(iTelServer, phoneInfo.iName));
	
	// find the voice line & open it
	TInt lines = 0;
	RPhone::TLineInfo lineInfo;
	User::LeaveIfError(iPhone.EnumerateLines(lines));
	if (lines <= 0)
		User::Leave(KErrNotSupported);
	for (TInt ii = 0 ; ii < lines ; ii++)
		{
		User::LeaveIfError(iPhone.GetLineInfo(ii, lineInfo));
		if (lineInfo.iLineCapsFlags & RLine::KCapsVoice)
			{
			// found our voice line
			if (lineInfo.iStatus > RCall::EStatusIdle)
				User::Leave(KErrInUse);
			User::LeaveIfError(iLine.Open(iPhone, lineInfo.iName));
			break;
			}
		}
	if (iLine.SubSessionHandle() <= 0)
		User::Leave(KErrNotFound);
	
	// create a voice call (note it's not currently active)
	User::LeaveIfError(iCall.OpenNewCall(iLine, iCallName));
	}

//
// CSmsEventDial::StartL
// start the dial-out procedure, beginning
// with a check of the network, line/call status
//
void CSmsEventDial::StartL()
	{
	User::LeaveIfError(iLine.GetMobileLineStatus(iCallStatus));
	if (iCallStatus > RMobileCall::EStatusIdle)
		User::Leave(KErrInUse);
	
	// some product tsy implementations can't handle a dial request immediately following receiving an sms. so we wait a little bit..5 seconds in fact
	iTimeout.After(iStatus, 5000000);
	SetActive();
	iState = ESmsEventDial;
	}

void CSmsEventDial::RunL()
	{
	User::LeaveIfError(iStatus.Int()); // process the error elsewhere
	switch (iState)
		{
		case ESmsEventDial:
			{
			// dial the number
			iCall.Dial(iStatus, iNumber.iTelNumber);
			SetActive();
			iState = ESmsEventStatusChange;
			}
		break;
				
		case ESmsEventWatchStatus:
		case ESmsEventStatusChange:
			{
			User::LeaveIfError(iCall.GetMobileCallStatus(iCallStatus));
			if (iCallStatus > RMobileCall::EStatusIdle)
				{
				// keep the call going until it drops
				iCall.NotifyMobileCallStatusChange(iStatus, iCallStatus);
				SetActive();
				iState = ESmsEventWatchStatus;
				return;
				}
			}
		break;
		
		default:
		break;
		};
	}

//
// CSmsEventDial::RunError
// inform the observer we hit a prob. with execution of the dial state machine
//
TInt CSmsEventDial::RunError(TInt /*aError*/)
	{
	//TODO iObserver.EventComplete(aError); 
	return KErrNone;
	}

void CSmsEventDial::DoCancel()
	{
	switch (iState)
		{
		case ESmsEventDial:
			iTimeout.Cancel();
		break;
		
		case ESmsEventStatusChange:
			iCall.DialCancel();
		break;
		
		case ESmsEventWatchStatus:
			iCall.NotifyStatusChangeCancel();			
		break;
		
		default:
		break;
		};
	}

CSmsShellCommand* CSmsShellCommand::NewL(CCmdSms& aObserver, const TDesC& aCommandText, const TDesC& aSenderAddress)
	{
	CSmsShellCommand* self = new(ELeave) CSmsShellCommand(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL(aCommandText, aSenderAddress);
	CleanupStack::Pop(self);
	return self;
	}

CSmsShellCommand::CSmsShellCommand(CCmdSms& aObserver)
	: CSmsEventBase(aObserver)
	{
	}

void CSmsShellCommand::ConstructL(const TDesC& aCommandText, const TDesC& aSenderAddress)
	{
	iSmsSender.CreateL(aSenderAddress);
	// Set up environment for script
	iScriptEnv = CEnvironment::NewL(iObserver.Env());
	_LIT(KSmsSenderEnvironmentVariable, "SMS_SENDER");
	iScriptEnv->SetL(KSmsSenderEnvironmentVariable, iSmsSender);

	const TDesC* scriptName = NULL;
	_LIT(KFshellName, "C:\\shared\\fshellscript%d.script");
	_LIT(KPerlName, "C:\\shared\\perlscript%d.pl");
	if (aCommandText.MatchF(_L("#!fshell*")) == 0)
		{
		iExeName = _L("fshell.exe");
		scriptName = &KFshellName;
		}
	else if (aCommandText.MatchF(_L("#!perl*")) == 0)
		{
		iExeName = _L("perl.exe");
		scriptName = &KPerlName;
		}
	else
		{
		User::Leave(KErrArgument);
		}	
	
	RBuf8 buf;
	CleanupClosePushL(buf);
	buf.CreateL(aCommandText.Length());
	buf.Copy(aCommandText);

	RFile scriptFile;
	CleanupClosePushL(scriptFile);
	TInt nonce = 0;
	iObserver.Fs().MkDirAll(*scriptName);
	TInt err = KErrNone;
	do {
		iScriptFile.Format(*scriptName, nonce);
		nonce++;
		err = scriptFile.Create(iObserver.Fs(), iScriptFile, EFileShareAny|EFileWrite);
		} while (err == KErrAlreadyExists);
	User::LeaveIfError(scriptFile.Write(buf));
	CleanupStack::PopAndDestroy(2, &buf);
	}

void CSmsShellCommand::StartL()
	{
	iProcess.CreateL(iExeName, iScriptFile, iObserver.IoSession(), iObserver.Stdin(), iObserver.Stdout(), iObserver.Stderr(), *iScriptEnv);
	iProcess.Run(iStatus);
	SetActive();
	}

void CSmsShellCommand::DoCancel()
	{
	iProcess.Process().LogonCancel(iStatus);
	}

CSmsShellCommand::~CSmsShellCommand()
	{
	Cancel();
	iProcess.Close();
	if (!iObserver.Debug())
		{
		iObserver.Fs().Delete(iScriptFile);
		}
	delete iScriptEnv;
	iSmsSender.Close();
	}

void CSmsShellCommand::RunL()
	{
	TExitType exitType = iProcess.Process().ExitType();
	TInt exitReason = iProcess.Process().ExitReason();
	TBool ok = exitType == EExitKill && exitReason == 0;
	if (ok)
		{
		iObserver.Printf(_L("Script from %S completed ok.\r\n"), &iSmsSender);
		}
	else
		{
		TInt err = exitReason;
		if (err >= 0) err = KErrGeneral;
		switch (iProcess.Process().ExitType())
			{
			case EExitKill:
				iObserver.PrintError(err, _L("Script %S failed, returned %d"), &iScriptFile, exitReason);
				break;
			case EExitTerminate:
				iObserver.PrintError(err, _L("Script %S terminated with %d"), &iScriptFile, exitReason);
				break;
			case EExitPanic:
				{
				TExitCategoryName cat = iProcess.Process().ExitCategory();
				iObserver.PrintError(err, _L("Script %S panicked with %S %d"), &iScriptFile, &cat, exitReason);
				break;
				}
			default:
				break;
			}
		}
	delete this;
	}
