/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:  Implementation of Liw Device binding
*
*/


#include <config.h>
#include <value.h>
#include <interpreter.h>
#include <date_object.h>
#include <operations.h>
#include <liwServiceHandler.h>
#include <RTSecManager.h>
#include <RTSecMgrUtility.h>
#include <RTSecMgrScriptSession.h>
#include <RTSecMgrCommonDef.h>
#include <WidgetRegistryClient.h>
#include <PropertyNameArray.h>
#include <internal.h>
#include <liwvariant.h>
#include <liwbufferextension.h>

#include "DeviceLiwBinding.h"
#include "DeviceLiwPeer.h"
#include "DeviceBinding.h"
#include "ServiceObject.h"
#include "ServiceEventHandler.h"
#include "DeviceLiwInterface.h"
#include "DeviceLiwMap.h"
#include "DeviceLiwIterable.h"
#include "DeviceLiwResult.h"

using namespace KJS;
using namespace LIW;

// CONSTANTS
#define SAPISECURITYPROMPTNEEDED 0x0001
#define SAPIPROMPTLESS           0x0002
#define SAPIACCESSDENIED         0x0003

// TBD: This needs to be substituted with LIW const in LIW header
const TInt KMaxKeySize = 255;
_LIT8( KWildChar, "*" );

const TInt KTimeBufSize = 255;
_LIT( KLocaleDateTime, "%E,%D%N%Y  %1 %2, %3%-B %:0%J%:1%T%:2%S%+B" );

// JS time epoch 1st January 1970 (needed to offset from Symbian epoch)
_LIT(KJsEpoch, "19700000:000000.000000");


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

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

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::ConstructL
// ConstructL sencond phace construction.
//
// ----------------------------------------------------------------------------
//
void CDeviceLiwBinding::ConstructL()
    {
        m_critArr = new (ELeave) RCriteriaArray();
        m_serviceHandler = CLiwServiceHandler::NewL();

        RWidgetRegistryClientSession widgetregistry;
        TInt ret = widgetregistry.Connect();
        if ( ret != KErrNone && ret != KErrAlreadyExists )
            {
            User::Leave( ret );
            }
        else
            {
            CleanupClosePushL( widgetregistry );
            }
        TInt aPolicyId = widgetregistry.SecurityPolicyId();
        if ( aPolicyId < 0 )
            {
            User::Leave( aPolicyId );
            }
        User::LeaveIfError(widgetregistry.Disconnect());
        CleanupStack::PopAndDestroy(); //widgetregistry

        // we need to keep main session allive, to keep sub session
        m_secMgr = CRTSecManager::NewL();
        
        CTrustInfo* trust = CTrustInfo::NewLC();

        // TODO: in future, to support blanket permission,
        // this function should be called at installation time.
        // TInt32 exId = secMgr->RegisterScript( aPolicyId, *trust );
        // m_scriptSession = secMgr->GetScriptSession( aPolicyId, exId );

        // check for script session validity.
        m_scriptSession = m_secMgr->GetScriptSessionL( aPolicyId, *trust );
        if ( !m_scriptSession )
            {
            User::Leave( KErrGeneral );
            }
        CleanupStack::PopAndDestroy( trust );
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::~CDeviceLiwBinding
// Default destructor
//
// ----------------------------------------------------------------------------
//
CDeviceLiwBinding::~CDeviceLiwBinding()
    {
    m_critArr->ResetAndDestroy();
    m_serviceHandler->DetachL(*m_critArr);
    m_critArr->ResetAndDestroy();
    delete m_critArr;
    delete m_serviceHandler;
    delete m_scriptSession;
    delete m_secMgr; 
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::LoadServiceProvider
// Load service provider
//
// ----------------------------------------------------------------------------
//
TInt CDeviceLiwBinding::LoadServiceProvider( ExecState* exec, const List& args )
    {
    TInt error = KErrNone;
    CLiwCriteriaItem* item;
    HBufC8* svcName = NULL;
    HBufC8* interfaceName = NULL;
    int argcount = args.size();

    if ( argcount > 0 && args[0]->type() == StringType &&
        args[0]->toString( exec ).size() > 0 )
        {
            TRAP( error,
            {
            // Get service name
            svcName = KJS::GetAsciiBufferL( args[0]->toString( exec ) );
            CleanupStack::PushL( svcName );

            // Get optional supported interface
            if ( argcount > 1 && args[1]->type() == StringType &&
                args[1]->toString( exec ).size() > 0 )
                {
                interfaceName = KJS::GetAsciiBufferL( args[1]->toString( exec ) );
                CleanupStack::PushL( interfaceName );
                }

            if ( interfaceName && interfaceName->Length() > 0 )
                {
                item = CLiwCriteriaItem::NewLC( 1, *interfaceName, *svcName );
                }
            else
                {
                item = CLiwCriteriaItem::NewLC( 1, KNullDesC8(), *svcName );
                }

            item->SetServiceClass( TUid::Uid( KLiwClassBase ) );
            RCriteriaArray crit_arr;
            crit_arr.AppendL( item );
            
            CleanupStack::Pop(item); // item

            // "Attach" runtime to service
            if ( m_scriptSession && m_serviceHandler )
                {
                TInt load_err;
                RWidgetRegistryClientSession widgetregistry;
                TInt ret = widgetregistry.Connect();
                if ( ret != KErrNone && ret != KErrAlreadyExists )
                    {
                    User::Leave( ret );
                    }
                else
                   {
                   CleanupClosePushL( widgetregistry );
                   }

                switch ( widgetregistry.WidgetSapiAccessState(m_Uid))
                    {
                    case SAPISECURITYPROMPTNEEDED :
                        load_err = m_serviceHandler->AttachL( crit_arr, *m_scriptSession );
                        break;
                    case SAPIPROMPTLESS :
                        load_err = m_serviceHandler->AttachL( crit_arr );
                        break;
                    case SAPIACCESSDENIED : 
                        load_err = KLiwSecurityAccessCheckFailed;
                        break;
                    default :
                        load_err = KLiwSecurityAccessCheckFailed;
                        break;
                    }
                
                User::LeaveIfError(widgetregistry.Disconnect());
                CleanupStack::PopAndDestroy(); //widgetregistry
                // hard coded for now since the definition of TLiwLoadStatus is not exposed to common dir yet
                if ( load_err == KLiwServiceLoadSuccess || load_err == KLiwServiceAlreadyLoaded )
                    {
                    error = KErrNone; // normalize the error
                    m_critArr->AppendL( item );
                    }
                else 
                    {
                    error = load_err; // pass on the TLiwLoadStatus
                    delete item;
                    }
                }

            if ( interfaceName )
                {
                CleanupStack::PopAndDestroy( interfaceName );
                }

            CleanupStack::PopAndDestroy( svcName );
            });
        }
    else
        {
        error = KErrArgument;
        }

    return error;
    }

// ---------------------------------------------------------------------------
// Convert Unload service provider
// return JSValue - javascript list
// ---------------------------------------------------------------------------
//
void CDeviceLiwBinding::UnloadServiceProvider(const TDesC8& aServiceName,const TDesC8& aInterfaceName)
    {
    TRAP_IGNORE(
        CLiwCriteriaItem* item = CLiwCriteriaItem::NewLC( 1, aInterfaceName, aServiceName );
        item->SetServiceClass( TUid::Uid( KLiwClassBase ) );
        RCriteriaArray critArray;
        critArray.AppendL( item );
        m_serviceHandler->DetachL( critArray );
        CleanupStack::Pop(item);
        critArray.ResetAndDestroy();
        for (int i = 0; i < m_critArr->Count(); i++)
            {
            if ( (*m_critArr)[i]->ServiceCmdStr() == aInterfaceName &&
                 (*m_critArr)[i]->ContentType() == aServiceName)
                m_critArr->Remove(i);
            }
        )
    }

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::ServiceObjectPeer
// Factory method to create a peer with service name
//
//
// ----------------------------------------------------------------------------
//
MDevicePeer* CDeviceLiwBinding::ServiceObjectPeer(
    ExecState *exec,
    const List& args )
    {
    DeviceLiwPeer* peer = NULL;

    TRAPD( error,
        {
        peer = new ( ELeave ) DeviceLiwPeer(
            exec->lexicalInterpreter()->globalExec(), this, 0 );
        CleanupStack::PushL( peer );
        peer->SetServiceNameL( exec, args );
        CleanupStack::Pop(peer);
        });

    if ( error != KErrNone )
        {
        return 0;
        }

    return static_cast<MDevicePeer*>( peer );
    }

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::ListProviders
// Call/Invoke the LIW function to list the providers
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::ListProviders(
    ExecState* exec,
    const List& args )
    {
    CLiwCriteriaItem* item = NULL;
    HBufC8* cmdName = NULL;
    HBufC8* contentName = NULL;
    int argcount = args.size();

    // Get content name
    if ( argcount > 0 && args[0]->type() == StringType &&
        args[0]->toString( exec ).size() > 0 )
        {
        contentName = KJS::GetAsciiBufferL( args[0]->toString( exec ) );
        CleanupStack::PushL( contentName );
        }

    if ( argcount > 1 && args[1]->type() == StringType &&
        args[1]->toString( exec ).size() > 0 )
        {
        // Get cmd name
        cmdName = KJS::GetAsciiBufferL( args[1]->toString( exec ) );
        CleanupStack::PushL( cmdName );
        }

    if ( cmdName && cmdName->Length() > 0 )
        {
        if ( contentName && contentName->Length() > 0 )
            {
            item = CLiwCriteriaItem::NewLC( 1, *cmdName, *contentName );
            }
        else
            {
            item = CLiwCriteriaItem::NewLC( 1, *cmdName, KWildChar() );
            }
        }
    else
        {
        if ( contentName && contentName->Length() > 0 )
            {
            item = CLiwCriteriaItem::NewLC( 1, KWildChar(), *contentName );
            }
        else
            {
            item = CLiwCriteriaItem::NewLC( 1, KWildChar(), KWildChar() );
            }
        }

    RCriteriaArray critArray, providerList;
    item->SetServiceClass( TUid::Uid( KLiwClassBase ) );
    critArray.AppendL( item );
    m_serviceHandler->QueryImplementationL( critArray, providerList );
    CleanupStack::Pop();//item

    if ( contentName )
        {
        CleanupStack::PopAndDestroy();//contentName
        }

    if ( cmdName )
        {
        CleanupStack::PopAndDestroy();//cmdName
        }

    if ( providerList.Count() > 0 )
        {
        // convert to js value
        List jsList;
        jsList.append( jsNumber( providerList.Count() ) );
        JSObject * rval = exec->lexicalInterpreter()->builtinArray()->construct(
                                                            exec, jsList );

        for ( TInt i = 0; i < providerList.Count(); i++ )
            {
            List jsPair;
            jsPair.append( jsNumber( 2 ) );
            JSObject * pairVal = exec->lexicalInterpreter()->builtinArray()->construct(
                                                            exec, jsPair );

            HBufC* buf1 = HBufC::NewLC( providerList[i]->ServiceCmdStr().Length() + 1 ); // +1 for zero terminate
            buf1->Des().Copy( providerList[i]->ServiceCmdStr() );
            buf1->Des().ZeroTerminate();
            pairVal->put( exec, Identifier::from(0), jsString( UString( (UChar*) buf1->Des().Ptr(), buf1->Des().Length() ) ) );

            HBufC* buf2 = HBufC::NewLC( providerList[i]->ContentType().Length() + 1 ); // +1 for zero terminate
            buf2->Des().Copy( providerList[i]->ContentType() );
            buf2->Des().ZeroTerminate();
            pairVal->put( exec, Identifier::from(1),
            jsString( UString( (UChar*) buf2->Des().Ptr(), buf2->Des().Length() ) ) );

            rval->put( exec, Identifier::from(i), pairVal );
            CleanupStack::PopAndDestroy(2);//buf1,buf2
            }

        critArray.ResetAndDestroy();
        providerList.ResetAndDestroy();

        return( rval );
        }

    critArray.ResetAndDestroy();
    providerList.ResetAndDestroy();
    return jsUndefined();
    }

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::InvokeOpInternal
// Call/Invoke the SAPI function
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::InvokeOpInternal(
    ExecState* exec,
    const Identifier& propertyName,
    const List& args,
    MDevicePeer* peer )
    {
    TUint cmdOption = 0;
    JSValue* rval = jsUndefined();
    int argcount = args.size();
    DeviceLiwPeer* callBack = NULL;

    if ( argcount > 1 )
        {
        cmdOption = KLiwOptASyncronous;
        if( args[1]->isObject() )
            {
            callBack = static_cast<DeviceLiwPeer*>( peer );
            }
        }
    if (propertyName == "Cancel")
        {
        cmdOption = KLiwOptCancel;
        }
    // Must have at least an operation name
    if ( propertyName.ustring().size() > 0 )
        {
        // Operation on a service interface object
        if ( peer && static_cast<DeviceLiwPeer*>( peer )->Interface() )
            {
            TRAPD( error,
                {
                CLiwGenericParamList* inps = CLiwGenericParamList::NewL(); // We manage this memory
                CleanupStack::PushL( inps );
                CLiwGenericParamList* outps = CLiwGenericParamList::NewL(); // And this
                CleanupStack::PushL( outps );

                // Collect args from first parameter. Note that args are
                // passed as an associative array.
                if ( argcount >= 1 )
                    {
                    JsList2LiwGenericParamListL( exec, args[0], *inps );
                    }

                // Get the operation name
                HBufC8* oper = KJS::GetAsciiBufferL( propertyName.ustring() );
                CleanupStack::PushL( oper );

                // Call the interface operation

                TTrapHandler* ____t = User::MarkCleanupStack();
                static_cast<DeviceLiwPeer*>( peer )->Interface()->ExecuteCmdL(
                                            *oper, *inps, *outps, cmdOption,
                                            callBack );
                User::UnMarkCleanupStack(____t);
                inps->Reset();
                rval = LiwGenericParamList2JsArray( exec, outps );
                outps->Reset();
                CleanupStack::PopAndDestroy( oper ); // map
                CleanupStack::Pop();// outps
                CleanupStack::PopAndDestroy(  );// inps
               });

            if ( error != KErrNone )
                {
                return throwError(exec, GeneralError);
                }
            }
        }

    return rval;
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::InvokeOp
// Call/Invoke the SAPI function
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::InvokeOp(
    ExecState* exec,
    const Identifier& propertyName,
    const List& args,
    MDevicePeer* peer )
    {
    return InvokeOpInternal( exec, propertyName, args, peer );
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::CreateInterface
// Create SAPI interface
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::CreateInterface(
    ExecState* exec, HBufC8* serviceName, const Identifier& interfaceName )
    {
    JSValue* rval = jsUndefined();
    TInt error = KErrNone;

    if ( m_serviceHandler && serviceName->Length() > 0 && interfaceName.ustring().size() > 0 )
        {
        // Since this is called from JS context, set up our own trap harness
        TRAP( error,
            {
            CLiwGenericParamList& inps = m_serviceHandler->InParamListL(); // LIW manages this memory
            CleanupStack::PushL( &inps );
            CLiwGenericParamList& outps = m_serviceHandler->OutParamListL(); // LIW manages this memory
            CleanupStack::PushL( &outps );

            HBufC8* ifaceName = KJS::GetAsciiBufferL( interfaceName.ustring() );
            CleanupStack::PushL( ifaceName );

            const HBufC8* hsvc = serviceName;

            // Create LIW params
            CLiwCriteriaItem* item = CLiwCriteriaItem::NewLC( 1, *ifaceName,
                                        TPtrC8( hsvc->Ptr(), hsvc->Size() ) );

            item->SetServiceClass( TUid::Uid( KLiwClassBase ) );

            // Call the service command operation
            m_serviceHandler->ExecuteServiceCmdL(
                *item, inps, outps, NULL );

            // Convert results into JS array
            rval = LiwGenericParamList2JsArray( exec, &outps );
            CleanupStack::PopAndDestroy( 2 ); //item, ifaceName
            CleanupStack::Pop( 2 );//inps, outps
        });

        if ( error != KErrNone)
            {
            return throwError(exec, GeneralError);
            }
        }

    return rval;
    }


//TBD: replace this with a generic object type discovery method

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::IsAList
// Check if javascript list
//
//
// ----------------------------------------------------------------------------
//
TBool CDeviceLiwBinding::IsAList( ExecState* exec, JSValue* aValue )
    {
    if(!aValue->isObject())
        return EFalse;
    PropertyNameArray propertyNames;
    aValue->toObject( exec )->getPropertyNames( exec, propertyNames );
    unsigned size = static_cast<unsigned>(propertyNames.size());

    for (unsigned i = 0; i < size; i++)
    {
        bool rval;
        propertyNames[i].toArrayIndex( &rval );
        if ( rval )
            {
            return ETrue;
            }
    }

    return EFalse;
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::JsVal2LiwVariant
// Convert from javascript value to LiwVariant
//
//
// ----------------------------------------------------------------------------
//
TBool CDeviceLiwBinding::JsVal2LiwVariant(
    ExecState* exec,
    JSValue* aValue,
    TLiwVariant& variant )
    {
    TBool ret = ETrue;
    TInt error = KErrNone;

    switch ( aValue->type() )
        {
        case StringType:
            {
            // Pass as TPtrC and let SAPI do the conversion to 8 bit if needed.
            TRAP( error,
                {
                variant.SetL( TLiwVariant(
                    TPtrC16( ( unsigned short* ) aValue->toString( exec ).data(),
                    aValue->toString( exec ).size() ) ) );
                });
            break;
            }
        case NumberType:
            {
            if (isNaN(aValue->getNumber()) || isInf(aValue->getNumber()))
            {
                ret = EFalse;
                break;
            }
            TInt32 intVal = aValue->toInt32( exec );
            double doubleVal = aValue->toNumber( exec );
            if ( intVal != doubleVal )
            {
                variant.Set( (TReal)doubleVal );
            }
            else
            {
                // this is not fully correct due to the limitation of js number
                variant.Set( intVal );
            }
            break;
            }
        case ObjectType:
            {
            JSObject* anObj = aValue->toObject( exec );

            // Handle date
            if(anObj->className().find(UString("Date"),0) == 0)
                {
                double d = aValue->toNumber( exec );  // get UTC milli seconds
                if( isNaN(d) || isInf(d) )
                {
                    ret = EFalse;
                    break;
                }

                //Get the JS time in integer seconds
                const TInt64 dateVal = d;

                // Set JS epoch offset from Symbian epoch
                TTime sTime(KJsEpoch);
                TTimeIntervalMicroSeconds dateMicroSecs(dateVal*1000);  // convert to UTC micro seconds
                sTime += dateMicroSecs;

                // uncomment following to debug time
                //TBuf<100> date;
                //sTime.FormatL(date,KLocaleDateTime);

                // Set the LIW Variant time
                variant.Set( sTime );

                break;
            }
            // Boolean object
            else if(anObj->className().find(UString("Boolean"),0) == 0)
                {
                TBool val = aValue->toNumber( exec );
                variant.Set( val );
                break;
                }
            // Number object
            else if(anObj->className().find(UString("Number"),0) == 0)
                {
                double d = aValue->toNumber( exec );
                if (isNaN(d) || isInf(d))
                    {
                    ret = EFalse;
                    break;
                    }
                TInt32 intVal = aValue->toInt32( exec );
                if ( intVal != d )
                    {
                    variant.Set( (TReal)d );
                    }
                else
                    {
                    // this is not fully correct due to the limitation of js number
                    variant.Set( intVal );
                    }
                break;
                }
            // String object
            else if(anObj->className().find(UString("String"),0) == 0)
                {
                // Pass as TPtrC and let SAPI do the conversion to 8 bit if needed.
                TRAP( error,
                    {
                    variant.SetL( TLiwVariant(
                        TPtrC16( ( unsigned short* ) aValue->toString( exec ).data(),
                        aValue->toString( exec ).size() ) ) );
                    });
                break;
                }
            PropertyNameArray prop_names;
            aValue->toObject( exec )->getPropertyNames( exec, prop_names );
            unsigned size = static_cast<unsigned>(prop_names.size());

            // handle array
            if ( IsAList( exec, aValue ) )
                {
                CLiwList* liwList = NULL;

                TRAP( error, { liwList = CLiwDefaultList::NewL(); } );
                if ( error != KErrNone )
                    {
                    break;
                    }
                CleanupStack::PushL( liwList );

                for (unsigned i = 0; i < size; i++)
                    {
                    bool rval;
                    prop_names[i].toArrayIndex( &rval );

                    if ( rval )
                        {
                        TLiwVariant vv;
                        JSValue* v = aValue->toObject( exec )->get(exec, prop_names[i]);

                        if ( JsVal2LiwVariant( exec, v, vv ) )
                            {
                            TRAP( error, { liwList->AppendL( vv ); } );  // makes a copy
                            vv.Reset();

                            if ( error != KErrNone )
                                {
                                break; //from while
                                }
                            }
                        }
                    }//end of while

                if ( error == KErrNone )
                    {
                    if( liwList->Count() ) // do not set variant if list is empty
                        {
                        TRAP( error, { variant.SetL( liwList ); } );
                        }
                    }

                CleanupStack::Pop( liwList );
                liwList->DecRef();
                }
            // handle other objects
            else
                { // Map
                TRAP( error,
                    {
                    CLiwMap* liwMap = CLiwDefaultMap::NewL();
                    CleanupClosePushL( *liwMap );

                    for (unsigned i = 0; i < size; i++)
                        {
                        HBufC8* keyName = KJS::GetAsciiBufferL(
                                        prop_names[i].ustring() );
                        CleanupStack::PushL( keyName );

                        TLiwVariant vv;
                        JSValue* v = aValue->toObject( exec )->get(exec, prop_names[i]);

                        if (!keyName || !keyName->Length() )
                            continue;

                        if ( !JsVal2LiwVariant( exec, v, vv )  )
                            {
                            // error converting js value to liw variant, throw debugging error
                            UString err_txt( "Invalid value:[" );
                            err_txt.append( v->toString(exec) );
                            err_txt.append( "] for key:[" );
                            err_txt.append( (const char *)keyName->Des().PtrZ() );
                            err_txt.append( "].\0" );
                            throwError( exec, GeneralError, err_txt );
                            }
                            else if ( !vv.IsEmpty() )
                            {
                            liwMap->InsertL( *keyName, vv );//makes a copy
                            vv.Reset();
                            }

                        CleanupStack::PopAndDestroy( keyName );
                        }

                    if( liwMap->Count() ) //do notset variant if map is empty
                        {
                        variant.SetL( liwMap );
                        }
                    CleanupStack::PopAndDestroy(); // this will call liwMap->close
                    });
                }
            break;
            }
        case BooleanType:
            {
            TBool val = aValue->toNumber( exec );
            variant.Set( val );
            break;
            }
        case NullType:
        case UnspecifiedType:
        case UndefinedType:
        default:
            ret = EFalse;
            break;
        }

    if ( error != KErrNone )
        {
        ret = EFalse;
        }

    return ret;
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::DeviceLiwVariant2JsVal
// Convert from LiwVariant to javascript value of the same type
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::LiwVariant2JsVal(
    ExecState* exec,
    const TLiwVariant& variant )
    {
    switch ( variant.TypeId() )
        {
        case EVariantTypeTInt32:
            {
            return jsNumber( variant.AsTInt32() );
            }
        case EVariantTypeDesC:
            {
            return jsString( UString(
                (UChar*) variant.AsDes().Ptr(), variant.AsDes().Length() ) );
            }
        case EVariantTypeDesC8:// this is something wrong
            {
            JSValue* rval = jsUndefined();
            TRAPD( error,
                {
                HBufC* buf = HBufC::NewLC( variant.AsData().Length() + 1 ); // +1 for zero terminate
                TPtr bufDes = buf->Des();
                bufDes.Copy( variant.AsData() );
                bufDes.ZeroTerminate();
                rval = jsString( UString( (UChar*) bufDes.Ptr(), bufDes.Length() ) );
                CleanupStack::PopAndDestroy();//buf
                });
                return rval;
            }
        case EVariantTypeList:
            {
            TLiwVariant v;
            List jsList;
            jsList.append( jsNumber( variant.AsList()->Count() ) );
            JSObject * rval = exec->lexicalInterpreter()->builtinArray()->construct(
                exec, jsList );

            for ( TInt i = 0; i < variant.AsList()->Count(); i++ )
                {
                TRAPD( error, { variant.AsList()->AtL( i, v ); } );
                rval->put(
                    exec, Identifier::from(i), LiwVariant2JsVal( exec, v ) );
                }
            v.Reset();
            return rval;
            }
        case EVariantTypeMap:
            {
            TLiwVariant v;
            List jsList;
            jsList.append( jsNumber( variant.AsMap()->Count() ) );
            JSObject * rval = new DeviceLiwMap(exec->lexicalInterpreter()->builtinArray()->construct(
                exec, jsList ), variant.AsMap(), this);

            for ( TInt i = 0; i < variant.AsMap()->Count(); i++ )
                {
                TBuf8<KMaxKeySize> name;
                TRAPD( error,
                    {
                    if ( variant.AsMap()->AtL( i, name ) )
                        {
                        variant.AsMap()->FindL( name, v );
                        rval->put( exec,
                                Identifier( ( const char* ) name.PtrZ() ),
                                LiwVariant2JsVal( exec, v ) );
                        }
                    });
                // No error processing
                }
            v.Reset();
            return rval;
            }
        case EVariantTypeInterface:
            {
            // new DeviceLiwPeer() might fail, in which case the service JSObject * peer will be null
            DeviceLiwPeer* peer = new DeviceLiwPeer(
                                exec->lexicalInterpreter()->globalExec(),
                                this, variant.AsInterface() );
            if ( !peer )
                {
                return jsUndefined();
                }

            JSObject* impl = new DeviceLiwInterface( exec, this, peer );
            return impl;
            }
        case EVariantTypeIterable:
            {
            return new DeviceLiwIterable(exec, this, variant.AsIterable());
            }
        case EVariantTypeTReal:
            {
            TReal realValue;
            variant.Get( realValue );
            return jsNumber( realValue );
            }
        case EVariantTypeTBool:
            {
            TBool boolValue;
            variant.Get( boolValue );
            return jsBoolean( boolValue );
            }
            //Note that unmarshaling to String here is not symetrical with marshaling from Date
            //but JS application can easily construct a Date if it wants one
        case EVariantTypeTTime:
            {
            TTime UTCTime;
            TBuf<KTimeBufSize> buf;
            variant.Get( UTCTime );

            TTime utcNow;
            utcNow.UniversalTime();
            TTime localNow;
            localNow.HomeTime();
            // convert UTC time to local time
            TTimeIntervalMicroSeconds diff = localNow.MicroSecondsFrom(utcNow);
            TTime localTime = UTCTime + diff;
            // format the time to output string
            localTime.FormatL( buf, KLocaleDateTime );
            const UChar *p = (const UChar*)buf.Ptr();
            return jsString( UString( p, buf.Length() ) );
            }
        case EVariantTypeBuffer: // this is something i don't what to do
            {
            return jsUndefined();
                /*
            CLiwBuffer* buf = variant.AsBuffer();
            switch (buf->TypeID())
                {
                case KLiwBufferFile:
                    {
                    RFile file = ((CLiwFileBuffer*)buf)->AsFile();
                    TFileName fileName;
                    file.FullName(fileName);
                    return jsString( UString((UChar*) fileName.Ptr(), fileName.Length() ) );
                    }
                case KLiwBufferBitmap:
                    {
                    return jsUndefined();
                    }
                default:
                    return jsUndefined();
                }*/
            }
        case EVariantTypeTUid:
        case EVariantTypeFileHandle:
        default:
            return jsUndefined();
        }
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::JsList2LiwGenericParamListL
// Convert javascript array (list) to LiwGenericParamList
//
//
// ----------------------------------------------------------------------------
//
void CDeviceLiwBinding::JsList2LiwGenericParamListL(
    ExecState* exec,
    JSValue* aValue,
    CLiwGenericParamList& aLiwList )
    {
    if (!aValue->isObject())
        return;
    PropertyNameArray propertyNames;
    aValue->toObject( exec )->getPropertyNames( exec, propertyNames );
    int size = static_cast<unsigned>(propertyNames.size());

    for (unsigned i = 0; i < size; i++)
        {
        HBufC8* name = KJS::GetAsciiBufferL(propertyNames[i].ustring() );
        if ( !name->Length() )
            break;
        CleanupStack::PushL( name );
        JSValue* v = aValue->toObject( exec )->get( exec, propertyNames[i] );;
        TLiwVariant vv;

        if (  !JsVal2LiwVariant( exec, v, vv ) ) // error occurred
            {
            throwError(exec, GeneralError);
            }
        else if ( !vv.IsEmpty() )
            {
            aLiwList.AppendL( TLiwGenericParam( *name, vv ) );
            vv.Reset();
            }

        CleanupStack::PopAndDestroy(); //name
        }
    }


// ----------------------------------------------------------------------------
// CDeviceLiwBinding::LiwGenericParamList2JsArray
// Convert LiwGenericParamList to javascript array (list)
//
//
// ----------------------------------------------------------------------------
//
JSValue* CDeviceLiwBinding::LiwGenericParamList2JsArray(
    ExecState* exec,
    CLiwGenericParamList* aLiwList )
    {
    // the output param list should not be empty, if it is, return an undefined js obj.
    if ( aLiwList->Count() == 0 )
        return jsUndefined();

    List jsList;

    jsList.append( jsNumber( aLiwList->Count() ) );

    JSObject * rval = new DeviceLiwResult(exec->lexicalInterpreter()->builtinArray()->construct(
                exec, jsList ));

    for ( TInt i = 0; i < aLiwList->Count(); i++ )
        {
        TBuf8<KMaxKeySize> name( (*aLiwList)[i].Name() );
        rval->put( exec, Identifier( (const char*) name.PtrZ() ),
            LiwVariant2JsVal( exec, (*aLiwList)[i].Value() ) );  //??? should call AtL?
        }

    return rval;
    }

// ----------------------------------------------------------------------------
// CDeviceLiwBinding::SetUid
// Sets the widget uid
//
//
// ----------------------------------------------------------------------------
//
void CDeviceLiwBinding::SetUid( const TUint& aValue)
    {
    m_Uid.iUid = aValue;
    }

// ----------------------------------------------------------------------------
// KJS::GetAsciiBufferL
// Convert the Unicode string to plain ASCII chars chopping of any higher bytes
//
//
// ----------------------------------------------------------------------------
//
HBufC8* KJS::GetAsciiBufferL( const UString& aBuf )
    {
    int length = aBuf.size();
    HBufC8* buf = HBufC8::NewL( length + 1 ); // +1 for zero terminate
    TPtr8 bufDes8 = buf->Des();
    const UChar* p = aBuf.data();
    const UChar *limit = p + length;

    while ( p != limit )
        {
        TChar c = p->uc;
        bufDes8.Append( c );
        ++p;
        }

    bufDes8.ZeroTerminate();
    return buf;
    }

//END OF FILE
