/*
* Copyright (c) 2002-2007 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 <AknQueryDialog.h>
#include <aknmemorycardui.rsg>
#include <StringLoader.h>
#include <aknsoundsystem.h>
#include <SecondaryDisplay/AknSecondaryDisplayDefs.h>
#include "AknCapServerEntry.h"
#include "AknCapServerDefs.h"
#include "AknMemoryCardDialogImpl.h"

class CAknCodeQuery: public CAknTextQueryDialog
    {
public:
    CAknCodeQuery(
        TDes& aDataText, 
        const CAknTextQueryDialog::TTone& aTone = CAknTextQueryDialog::ENoTone);
        
    TBool OkToExitL(TInt aButtonId);
    void TryExit(TInt aButtonId); 
    void SetObserver(CAknCodeQuery** aSelf, CAknMMCPasswordRequester* aObserver );
    ~CAknCodeQuery(){};
    
private:
    CAknMMCPasswordRequester* iObserver;
    CAknCodeQuery** iSelf;
    };

CAknMMCPasswordRequester::CAknMMCPasswordRequester()
    {
    }

CAknMMCPasswordRequester* CAknMMCPasswordRequester::NewL()
    {
    CAknMMCPasswordRequester* self = new (ELeave) CAknMMCPasswordRequester();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(); //self
    return self;
    }

void CAknMMCPasswordRequester::ConstructL()
    {
    iResFileLdr.AddResourceFileL();
    }

CAknMMCPasswordRequester::~CAknMMCPasswordRequester()
    {
    if (iQuery)
        {
        delete iQuery;
        CompleteAllMessages(KErrDied);
        }

    iResFileLdr.DeleteResourceFile();

    delete iPrompt;
    iPrompt = NULL;
    
    iMessages.Close();
    }

void CAknMMCPasswordRequester::ClientExit( TInt aClientID, TBool aCompleteWithCancel )
    {
    for(TInt i = iMessages.Count()-1; i >= 0; i--)
        {
        if (iMessages[i].iClientId == aClientID)
            {
            if (aCompleteWithCancel)
                {
                iMessages[i].iMessage.Complete(KErrCancel);
                }
            iMessages.Remove(i);
            break;
            }
        }
    }
    
void CAknMMCPasswordRequester::CompleteAllMessages(TInt aReason)
   {
   for(TInt i = iMessages.Count()-1; i >= 0; i--)
        {
        iMessages[i].iMessage.Complete(aReason);
        iMessages.Remove(i);
        }
    }

void CAknMMCPasswordRequester::AppendQueueItemL(const RMessagePtr2& aMessage, TInt aClientID)
    {
    if (!aClientID)
        {
        // recursive call for start (there must be active message before this can occur)
        __ASSERT_ALWAYS(iMessages.Count(), User::Invariant());
        return; 
        }
    for(TInt i = iMessages.Count()-1; i >= 0; i--)
        {
        if (iMessages[i].iClientId == aClientID)
            {
            User::Leave(KErrAlreadyExists);
            }
        }
    User::LeaveIfError(iMessages.Append(TMessageWithClientId(aMessage, aClientID)));
    }   

void CAknMMCPasswordRequester::StartL(TInt aDrive, TBool aStore, const RMessagePtr2& aMessage, 
    TInt aClientID)
    {
    // Will leave if same client tries to activate new request while old pending, basically should
    // panic the client.
    AppendQueueItemL(aMessage, aClientID); 
    
    if (iQuery)
        {
        return;
        }

    iStore = aStore;
    iDriveNumber = aDrive;
    
    SetPromptL();

    iPassWd.FillZ(); // this is safe as we are derived from 
                     // CBase and this is done for every unlock operation
    iPassWd.Zero();
    
    iQuery = new (ELeave) CAknCodeQuery(iPassWd);
    iQuery->PrepareLC(R_MMC_PASSWORD_QUERY);
    iQuery->SetObserver(&iQuery,this);

    if (iPrompt)
        {
        iQuery->SetPromptL(*iPrompt);
        }
    
    CAknKeySoundSystem* sounds = iAvkonAppUi->KeySounds();
    // we are assuming that there is no need to bring sounds forwards more than once per operation
    if (iNumberOfAttempts == 0 && sounds ) 
        {
        sounds->BringToForeground();
        }

    iQuery->PublishDialogL(EAknMemoryCardQuery);
    iQuery->RunLD();
    // block apps key after it is sure that OkToExitL will be called for query
    ((CAknCapAppServerAppUi*)iAvkonAppUi)->SuppressAppSwitchingL(EAknAppsKeyBlockAddCount, 0);
    }

void CAknMMCPasswordRequester::Cancel(TInt aClientID)
    {
    if ( iQuery )
        {
        if (iMessages.Count() == 1)
            {
            iQuery->TryExit( EEikBidCancel ); // completes the message        
            }
        else
            {
            ClientExit(aClientID, ETrue );
            }
        }
    }

void CAknMMCPasswordRequester::SetPromptL()
    {
    delete iPrompt;
    iPrompt = NULL;

    if ( iNumberOfAttempts > 0 ) 
        {
        iPrompt = StringLoader::LoadL(R_QTN_MEM_CARD_UNLOCK_ERROR); 
        }
    else 
        {
        iPrompt = StringLoader::LoadL(R_QTN_MEM_CARD_UNLOCK_PROMPT);
        }
    }

void CAknMMCPasswordRequester::UnlockCard(TBool aAccept)
    {
    if ( aAccept ) 
        {
        TMediaPassword p;
        p.FillZ(KMaxMediaPassword); // not sure if fileserver uses buffer length correctly
        p.Zero();

        const TUint8 *pt8Src = reinterpret_cast<const TUint8 *>(iPassWd.Ptr());
        // We use this to check whether there is something else than zeros in ttext8 array left
        const TUint16 *pt16Src = iPassWd.Ptr();

        for (TInt j = 0; j < KMaxMediaPassword / 2 && pt16Src[j]; ++j)
            {
            p.Append(pt8Src[j << 1]);
            p.Append(pt8Src[(j << 1) + 1]);
            }

        iNumberOfAttempts++;
        TInt err = CCoeEnv::Static()->FsSession().UnlockDrive(iDriveNumber,p,ETrue);
#ifdef _DEBUG
        RDebug::Print(_L("CAknMMCPasswordRequester::Ulock ret: %D"),err);
#endif
        if (err == KErrNone || err == KErrAlreadyExists)
            {
            CompleteAllMessages(KErrNone);
            iNumberOfAttempts = 0;
            }
        else 
            {
            // borrow top most message, it is not used anyway                
            TRAPD (err, StartL(iDriveNumber,iStore,iMessages[0].iMessage)); 
            if (err)
                {
                CompleteAllMessages(err); // To ensure that old instance of query will be deleted. 
                }
            iNumberOfAttempts = 0;
            return;
            }
        }
    else // user or system cancel 
        {
        CompleteAllMessages(KErrCancel);
        iNumberOfAttempts = 0;
        }
    }

CAknCodeQuery::CAknCodeQuery(TDes& aDataText, const CAknTextQueryDialog::TTone& aTone)
    :CAknTextQueryDialog(aDataText, aTone)
    {
    }

TBool CAknCodeQuery::OkToExitL(TInt aButtonId)
    {
    TBool result = (aButtonId == EAknSoftkeyOk||aButtonId == EEikBidOk);

    if ( result )
        {
        CAknQueryControl* control = QueryControl();
        if (control)
            {
            control->GetText(iDataText);
            }
        }
    (*iSelf) = 0;

    iObserver->UnlockCard( result ); // synch, ie. query will be deleted after this returns !
    ((CAknCapAppServerAppUi*)iAvkonAppUi)->SuppressAppSwitchingL(EAknAppsKeyBlockDecreaseCount, 0);
    return ETrue;
    }
        
void CAknCodeQuery::SetObserver(CAknCodeQuery** aSelf, CAknMMCPasswordRequester* aObserver )
    {
    iObserver = aObserver;
    iSelf = aSelf;
    }

void CAknCodeQuery::TryExit(TInt aButtonId)
    { 
    TRAP_IGNORE( TryExitL(aButtonId));
    }

// End of file
