/*
* Copyright (c) 2004 - 2006 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 <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>
#include <apparc.h>
#include <coemain.h>
#include <coecntrl.h>
#include <w32std.h>
#include <BTMouseBitMaps.mbg>
#include "clientimagecommander.h"

#ifndef DBG
#ifdef _DEBUG
#define DBG(a) a
#else
#define DBG(a)
#endif
#endif

_LIT( KBTMOUSEMBMFileName, "\\resource\\apps\\BTMouseBitMaps.mbm" );


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

// -----------------------------------------------------------------------------
// RImageCommander::RImageCommander()
// Create a new animation object.
// -----------------------------------------------------------------------------
//
EXPORT_C RImageCommander::RImageCommander( RAnimDll &aAnimDll, RWsSession& aWsSession ) :
        RAnim( aAnimDll ), iSession(aWsSession),
        iBaseBitmap(NULL),
        iBaseMaskBitmap(NULL),
        iMouseButtonPressed(EFalse),
        iWakeUpCalculator(0)
    {
    DBG(RDebug::Print(_L("RImageCommander::RImageCommander")));

    // No implementation required
    }

EXPORT_C RImageCommander::~RImageCommander( )
    {
    iSprite.Close();

    if ( iPointBufferQueue )
        {
        delete iPointBufferQueue;
        iPointBufferQueue = NULL;
        }

    if ( iBaseBitmap)
        {
        delete iBaseBitmap;
        iBaseBitmap = NULL;
        }

    if ( iBaseMaskBitmap )
        {
        delete iBaseMaskBitmap;
        iBaseMaskBitmap = NULL;
        }
    RAnim::Close();
    }

// -----------------------------------------------------------------------------
// RImageCommander::ImageConstruct()
// Construct a new animation object.
// -----------------------------------------------------------------------------
//
EXPORT_C void RImageCommander::ImageConstructL( RWindowGroup& aWindowGroup, TSize aSize )
    {
    // Send the parameters to the animation server object construction

    DBG(RDebug::Print(_L("RImageCommander::ImageConstructL IN")));

    iPointBufferQueue = NULL;
    iPointBufferQueue = CPointQueue::NewL(this,KMsgBTMouseBufferQueue);

    CreateSpriteL(aSize, aWindowGroup);

    DBG(RDebug::Print(_L("RImageCommander::ImageConstructL Construct Anim")));

    TPtrC8 des(NULL,0);
    RAnim::Construct( iSprite, 0, des );
    
    DBG(RDebug::Print(_L("RImageCommander::ImageConstructL OUT")));
    }

// ----------------------------------------------------------------------------
// RImageCommander::CreateSpriteL(TSize aSize, RWindowGroup& aGroup)
// ----------------------------------------------------------------------------
//
void RImageCommander::CreateSpriteL(TSize aSize, RWindowGroup& aGroup)
    {
    DBG(RDebug::Print(_L("RImageCommander::CreateSpriteL START")));
    TSpriteMember member;
    

    DBG(RDebug::Print(_L("RImageCommander::CreateSpriteL size %d,%d"), aSize.iWidth, aSize.iHeight));
    (void)aSize;

    iSprite=RWsSprite(iSession);
    TInt error = iSprite.Construct(aGroup, TPoint(),ESpriteNoChildClip|ESpriteNoShadows);
    User::LeaveIfError(error);
    iBaseBitmap = new (ELeave) CFbsBitmap;
    // Drive Letter:
    TFileName file(CEikonEnv::Static()->EikAppUi()->Application()->AppFullName().Left(2));
    // Path and file name:
    file.Append( KBTMOUSEMBMFileName ); // always safe

    User::LeaveIfError(iBaseBitmap->Load(file, EMbmBtmousebitmapsCursor));
    iBaseMaskBitmap = new (ELeave) CFbsBitmap;
    User::LeaveIfError(iBaseMaskBitmap->Load(file, EMbmBtmousebitmapsCursormask));
    FillInSpriteMember(member, iBaseBitmap, iBaseMaskBitmap);
    User::LeaveIfError(iSprite.AppendMember(member));
    
    DBG(RDebug::Print(_L("RImageCommander::CreateSpriteL END")));

    }

// ----------------------------------------------------------------------------
// RImageCommander::FillInSpriteMember(TSpriteMember& aMember, CFbsBitmap* icon, CFbsBitmap* iconMask)
// ----------------------------------------------------------------------------
//
void RImageCommander::FillInSpriteMember(TSpriteMember& aMember, CFbsBitmap* icon, CFbsBitmap* iconMask)
    {
    DBG(RDebug::Print(_L("RImageCommander::FillInSpriteMember"));

    if ( ( icon == NULL) || ( iconMask == NULL) )
        {
        RDebug::Print(_L("RImageCommander::FillInSpriteMember no icons !"));
        } 
    )
    aMember.iBitmap=icon;
    aMember.iMaskBitmap=iconMask;
    aMember.iInvertMask=EFalse;
    aMember.iDrawMode = CGraphicsContext::EDrawModeWriteAlpha;
    aMember.iOffset=TPoint(0,0);
    
    DBG(RDebug::Print(_L("RImageCommander::FillInSpriteMember END")));

    }

// -----------------------------------------------------------------------------
// RImageCommander::ImageCommand()
// Send a command (aCommand) to the animation server object.
// Because there is no way to return an error from the server side using this
// method, it is important that any server side code for these commands should
// not be able to fail or leave.  If the operation can fail, then the method
// TInt CommandReply(TInt aOpcode) should be used instead
// -----------------------------------------------------------------------------
//
EXPORT_C void RImageCommander::ImageCommand( TInt aCommand )
    {
    // Could optionally decode the commands here
    DBG(RDebug::Print(_L("RImageCommander::ImageCommand %d"), aCommand));
    if ( aCommand == KResetBTCursorAnim ) 
        {
        //reset the current location to avoid sudden jumping 
        iCurrentPoint.SetXY(0,0);
        }

    RAnim::Command( aCommand );
    
    DBG(RDebug::Print(_L("RImageCommander::ImageCommand OUT")));

    }

TRawEvent::TType RImageCommander::PointerEventToRawEvent(TPointerEvent::TType aEventType)
    {
    TRawEvent::TType aType;
    switch(aEventType)
        {
        case TPointerEvent::EButton1Down:
            {
            aType=TRawEvent::EButton1Down;
            break;
            }
        case TPointerEvent::EButton1Up:
            {
            aType=TRawEvent::EButton1Up;
            break;
            }
        case TPointerEvent::EMove:
            {
            aType=TRawEvent::EPointerMove;
            break;
            }
        case TPointerEvent::ESwitchOn:
            {
            aType=TRawEvent::EPointerSwitchOn;
            break;
            }
        default:
            {
            aType=TRawEvent::ENone;
            break;
            }
        }
    return aType;
    }

TInt RImageCommander::SendPointerEvent(TPointerEvent::TType aEventType)
    {
    DBG(RDebug::Print(_L("RImageCommander::SendPointerEvent")));

    TRawEvent rawEvent;
    TPoint point(iCurrentPoint);
    TRawEvent::TType type = PointerEventToRawEvent(aEventType);
    rawEvent.Set(type, point.iX, point.iY, 30);
    typedef TPckgBuf< TRawEvent >  TRawEventDataPckg;
    TRawEventDataPckg rawEventPckg(rawEvent);
    RAnim::Command( KSendRawEvent, rawEventPckg );
    return KErrNone;
    }

TBool RImageCommander::RestrictPos()
    {
    TBool notInScreen(EFalse);
    TPixelsAndRotation sizeAndRotation;
    sizeAndRotation.iPixelSize.SetSize(360,640); // Just a guess
    CWsScreenDevice* screen(CEikonEnv::Static()->ScreenDevice());
    if (screen)
        {
        TInt mode(screen->CurrentScreenMode());
        screen->GetScreenModeSizeAndRotation(mode, sizeAndRotation);
        }
    
    DBG(
        RDebug::Print(_L("RImageCommander::RestrictPos sizeAndRotation.iPixelSize (%d,%d)"), sizeAndRotation.iPixelSize.iWidth, sizeAndRotation.iPixelSize.iHeight);
        RDebug::Print(_L("RImageCommander::RestrictPos sizeAndRotation.iRotation (%d)"), sizeAndRotation.iRotation);
        )

    TRect validRect(sizeAndRotation.iPixelSize);
    if (! validRect.Contains(iCurrentPoint))
        {
        notInScreen = ETrue;
        if (iCurrentPoint.iX<0)
            iCurrentPoint.iX=0;
        else if (iCurrentPoint.iX>=validRect.iBr.iX)
            iCurrentPoint.iX=validRect.iBr.iX-1;

        if (iCurrentPoint.iY<0)
            iCurrentPoint.iY=0;
        else if (iCurrentPoint.iY>=validRect.iBr.iY)
            iCurrentPoint.iY=validRect.iBr.iY-1;
        }
    return notInScreen;
    }

inline TBool RImageCommander::CheckCurrentPoint()
    {
    DBG(TPoint pos(iCurrentPoint));

    TBool outSide(RestrictPos());
    
    DBG(
    if (outSide)
        {
       RDebug::Print(_L("RImageCommander::CheckCurrentPoint NOT in screen (%d,%d)"), pos.iX, pos.iY);
        }
    else
        {
       RDebug::Print(_L("RImageCommander::CheckCurrentPoint DOES in screen (%d,%d)"), pos.iX, pos.iY);
        }
    )
    return outSide;
    }

TInt RImageCommander::SendWakeUp()
    {
    TRawEvent rawEvent;
    rawEvent.Set(TRawEvent::ESwitchOn);
    return UserSvr::AddEvent(rawEvent);
    }

void RImageCommander::HandlePointL(const TPoint& aPoint)
    {
    DBG(RDebug::Print(_L("RImageCommander::HandlePointL pos (%d,%d)"),
                  aPoint.iX, aPoint.iY));

    if ( ( aPoint.iX != 0) || ( aPoint.iY != 0) )
        {
        iCurrentPoint += aPoint;
        TBool notInScreen(CheckCurrentPoint());
        if (iMouseButtonPressed)
            {
            // Dragging possible now:
            if (notInScreen)
                {
                SendPointerEvent(TPointerEvent::EButton1Up);
                iMouseButtonPressed = EFalse;
                }
            else
                {
                SendPointerEvent(TPointerEvent::EMove);
                }
            }
        else
            {
            typedef TPckgBuf< TPoint >  TPointDataPckg;
            TPointDataPckg pointPckg(iCurrentPoint);
            RAnim::Command( KChangeCursor, pointPckg );
            }
        }
    iWakeUpCalculator++;
    if (iWakeUpCalculator > KWakeUpNeeded)
        {
        SendWakeUp();
        iWakeUpCalculator = 0;
        }
    }

void RImageCommander::SendEventL(TPointerEvent::TType aEventType)
    {
    DBG(RDebug::Print(_L("RImageCommander::SendEventL (%d)"), aEventType));

    if (aEventType == TPointerEvent::EButton1Down)
        {
        iMouseButtonPressed = ETrue;
        SendWakeUp();
        }
    else if (aEventType == TPointerEvent::EButton1Up)
        {
        iMouseButtonPressed = EFalse;
        }
    SendPointerEvent(aEventType);
    }

// ======== class CPointQueue========
//
// ---------------------------------------------------------------------------
// CPointQueue::CPointQueue
// Constructor. Adds the active object to the active scheduler
// and issues a request for server notification.
// ---------------------------------------------------------------------------
//

CPointQueue::CPointQueue(MPointHandler* aHandler):CActive( EPriorityHigh ),
        iHandler(aHandler)
    {
    // issue asynchronous request and set this object active
    DBG(RDebug::Print(_L("(CCPointQueue::CPointQueue")));

    CActiveScheduler::Add( this );

    DBG( RDebug::Print(_L("(CCPointQueue::CPointQueue Out")));

    }

// ---------------------------------------------------------------------------
// CPointQueue::NewL
// factory constructor.
// ---------------------------------------------------------------------------
//
CPointQueue* CPointQueue::NewL(MPointHandler* aHandler,const TDesC& aName)
    {
    DBG(RDebug::Print(_L("(CCPointQueue::NewL")));

    CPointQueue* queue = new(ELeave)CPointQueue(aHandler);
    CleanupStack::PushL(queue);
    queue->ConstructL(aName);
    CleanupStack::Pop(queue);
    // Start receiving:
    queue->GetPoint();
    
    DBG(RDebug::Print(_L("(CCPointQueue::NewL Out")));

    return queue;
    }

// ---------------------------------------------------------------------------
// CPointQueue::ConstructL
// 2nd phase constructor
// ---------------------------------------------------------------------------
//
void CPointQueue::ConstructL(const TDesC& aName)
    {
    (void) aName;
    //Open message queue
    TInt err = iPointBufQueue.OpenGlobal(KMsgBTMouseBufferQueue);   
    if (err == KErrNotFound)
        {
        User::LeaveIfError(iPointBufQueue.CreateGlobal(KMsgBTMouseBufferQueue, KPointQueueLen));    
        }
    else 
        {
        User::LeaveIfError( err );
        } 
    }
// ---------------------------------------------------------------------------
// CPointQueue::CPointQueue
// Destructor. Cancels any outstanding requests
// ---------------------------------------------------------------------------
//
CPointQueue::~CPointQueue()
    {
    Cancel();
    iPointBufQueue.Close();
    }

// ---------------------------------------------------------------------------
// CPointQueue::DoCancel
// Cancels the notification requests
// ---------------------------------------------------------------------------
//
void CPointQueue::DoCancel()
    {
    iPointBufQueue.CancelDataAvailable();
    }

// ---------------------------------------------------------------------------
// CPointQueue::RunL
// ---------------------------------------------------------------------------
//
void CPointQueue::RunL()
    {
    DBG(RDebug::Print(_L("CCPointQueue::RunL")));

    TInt ret;
    //iHandler is always there. No need to check it
    TPointBuffer points;
    ret = iPointBufQueue.Receive(points);
    DBG(RDebug::Print(_L("CCPointQueue::RunL , ret = %d, points %d"), ret, points.iNum));

    if (ret == KErrNone)
        {
        TPoint pos(0,0);
        for(TInt ii = 0; ii < points.iNum; ii++)
            {
            pos += points.iPoint[ii];
            if ( ( points.iType[ii] == KBufferPenDown ) ||
                    ( points.iType[ii] == KBufferPenUp ) )
                {
                iHandler->SendEventL( ( points.iType[ii] == KBufferPenDown ) ?
                                      TPointerEvent::EButton1Down :
                                      TPointerEvent::EButton1Up );
                iHandler->HandlePointL(pos);
                pos.SetXY(0,0);
                }
            }
        iHandler->HandlePointL(pos);
        }
    // re-issue request
    GetPoint();
    }

// ---------------------------------------------------------------------------
// CPointQueue::RunError
// ---------------------------------------------------------------------------
//
TInt CPointQueue::RunError(TInt /*aError*/)
    {
    DBG(RDebug::Print(_L("CCPointQueue::RunError")));
    // re-issue request
    GetPoint();
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CPointQueue::GetPoint
// Reissue request for point
// ---------------------------------------------------------------------------
//
void CPointQueue::GetPoint()
    {
    DBG(RDebug::Print(_L("CCPointQueue::GetPoint")));

    if(!IsActive()) 
        {
        iPointBufQueue.NotifyDataAvailable(iStatus);
        SetActive();
        }
    DBG(RDebug::Print(_L("CCPointQueue::GetPoint Out")));
    }
// End of File
