/*global UNIFACE JSON */
/* %fv: uscope.js-8 % %dc: Thu Aug 20 15:19:04 2009 %*/

/******************************************************************************
COPYRIGHT
    (C) 2008 Compuware Corporation.  All rights reserved.
    Unpublished - rights reserved under the Copyright Laws of the United States.

    U.S. GOVERNMENT RIGHTS-Use, duplication, or disclosure by the
    U.S. Government is subject to restrictions as set forth in
    Compuware Corporation license agreement and as
    provided in DFARS 227.7202-1(a) and 227.7202-3(a) (1995),
    DFARS 252.227-7013(c)(1)(ii)(OCT 1988), FAR 12.212(a) (1995),
    FAR 52.227-19, or FAR 52.227-14 (ALT III), as applicable.
    Compuware Corporation

    This product contains confidential information and trade
    secrets of Compuware Corporation.  Use disclosure, or
    reproduction is prohibited
    without the prior express written permission of Compuware Corporation.
******************************************************************************/

/*******************************************************************************
date   refnum    version who description
090717 c27474    9401c1  mzu Split from udatalayer.
* date   refnum    version who description
*******************************************************************************/
(function(){

function nop(){}

// Merge 2 scope objects

function mergeScope(a_c1, a_c2)
{
    /*
    Since 'undefined' (default) means 'everything'
    */
    if (a_c2 === undefined || a_c1  === undefined) 
    {
        return undefined;
    }
        
    if (typeof a_c1 == "object" || typeof a_c2 == "object")
    {
        var i;
        if (a_c1.constructor == Array)
        {
            if (a_c1.length === 0)
            {
                return a_c2;
            }
            if (a_c2.length === 0)
            {
                return a_c1;
            }
            var l_a = [];

            for (i=0; i< a_c1.length; i++)
            {
                l_a.push(a_c1[i]);
            }
            for (i=0; i< a_c2.length; i++)
            {
                l_a.push(a_c2[i]);
            }
            return l_a;
        }
        else
        {
            var l_c = { };

            for (i in  a_c1) if (i != "selector" && a_c1.hasOwnProperty(i))
            {
                l_c[i] = a_c1[i];
            }
            for (i in a_c2) if (i != "selector" && a_c2.hasOwnProperty(i))
            {
                l_c[i] = mergeScope( a_c2[i], l_c[i]);
            }
            return l_c;
        }
    }
    else
    {
        return a_c1 || a_c2;
    }
}

/* 
Return true if a scope declaration is empty, i.e.
a scope/endscope block or begin/end block without contents - meaning 'nothing' 
on this level is in scope
Otherwise: false (currently meaning 'everything' on this level is in scope, but this will change
*/
function isEmptyScope(a_scope)
{

    if (a_scope === undefined)
    {
        return false;
    }
   
   var l_empty = false;
    if (a_scope.entities !== undefined)
    {
         if (a_scope.entities.length !== 0)
         {
            return false;
         }
         l_empty = true;
    }
    if (a_scope.fields !== undefined)
    {
        l_empty = (a_scope.fields.length === 0);
    }
    return l_empty;
}

/* 
Traverse the chain(s) of included scopes (e.g. using the 'operation' keyword in a scope declaration) and
create a list of input/output scopes for each relevant instance
Error conditions:
- operation does not exist
- more than one input or output scope for the same instance

Special condition:
- instance not loaded yet: this can only mean the whole instance is in scope and already blocked.
    The queueing mechanism is used to postpone the request, after which this function is called again to 
    determine the proper scope.
  This condition is signaled by having the instance name in the list with a scope of 'undefined'

*/
function traverseScopes(a_instanceName, a_scope)
{   
    var l_scopes = {};
    var l_operations = { };
    var l_instances = {};
    
    function addScope(a_instanceName, a_scope)
    {
        if (l_scopes[a_instanceName] === undefined)
        {
            // Default empty, add to it later
            l_scopes[a_instanceName] = { input: {entities:[]}, output: {entities:[]} };
        }
        
        if (a_scope === undefined)
        {
            l_scopes[a_instanceName].input = mergeScope(l_scopes[a_instanceName].input, undefined);        
            l_scopes[a_instanceName].output = mergeScope(l_scopes[a_instanceName].output, undefined);        
        }
        else
        {
            l_scopes[a_instanceName].input = mergeScope(l_scopes[a_instanceName].input, a_scope.input);        
            l_scopes[a_instanceName].output = mergeScope(l_scopes[a_instanceName].output, a_scope.output);        
            var i;
            if ( a_scope.include !== undefined)
            {
                for (i=0; i< a_scope.include.length; i++)
                {
                    // Guard against loops in the import graph
                    var l_fqn = a_scope.include[i].inst + "." + a_scope.include[i].oper;
                    if ( l_operations[l_fqn] ===undefined )
                        {
                        var l_existingScope = l_scopes[a_scope.include[i].inst];               
                        l_operations[l_fqn] = true;
                        // 1. Get component 
                        var l_inst = UNIFACE.components[a_scope.include[i].inst];
                        var l_operScope;
                        if (l_inst !== undefined && l_inst.definition !== undefined)
                        {
                            // 2. get operation scope
                            l_operScope = l_inst.definition.operations[a_scope.include[i].oper];
                            if (l_operScope === undefined)
                            {
                                UNIFACE.throwException("Error: instance " + a_scope.include[i].inst + " does not have an operation " + a_scope.include[i].oper);
                            }
                            l_operScope.scopeName = "operation " + l_fqn;
                            
                            if (l_existingScope !== undefined)
                            {
                                if (!isEmptyScope(l_existingScope.input) && !isEmptyScope(l_operScope.input))
                                {
                                    UNIFACE.throwException("Error: instance " + a_scope.include[i].inst + " is included multiple times with different input scope.");
                                }
                                if (!isEmptyScope(l_existingScope.output) && !isEmptyScope(l_operScope.output))
                                {
                                    UNIFACE.throwException("Error: instance " + a_scope.include[i].inst + " is included multiple times with different output scope.");
                                }
                            }
                        }
                        addScope(a_scope.include[i].inst, l_operScope);
                    }
                }
            }
        }
    }
    addScope(a_instanceName, a_scope);
    return l_scopes;
}

var Scope = {
    NONE        : "0",
    ALL         : "*",
    CURRENT     : ".",
    FIRST       : "1",
    LAST        : "n",
    MOD         : "mod",
    NEW         : "new",
    SPECIFIC    : "id"
};

/**
@return an array of occurrences that are in scope

Helper function to resolve occurrence selectors from a scope to a list
of actual affected occurrences.
*/
function findOccsInScope(a_realizedEntity, a_data, a_scope, a_currentPath )
{

    var l_occs = [];
    var l_incOccs = [];

    
    function addOcc(a_occ, scp)
    {
        a_occ.scp = mergeScope(scp, a_occ.scp);
        if (!a_occ.inc)
        {
            a_occ.inc = true;
            l_incOccs.push(a_occ);
        }
    }
    
    var i;
    for (i=0; i<a_data.occs.length; i++)
    {
        l_occs.push({stoc: null, daoc: a_data.occs[i], inc:false, scp: null });
    }    
    if (a_realizedEntity)
    {
        for (i=0; i<a_realizedEntity.occs.length && a_realizedEntity.occs[i].realized; i++)
        {                    
            l_occs[a_realizedEntity.occs[i].dataIdx].stoc = a_realizedEntity.occs[i];
        }
    }
        
    if (a_scope === undefined)
    {
        for (i =0; i< l_occs.length; i++)
        {
            addOcc(l_occs[i], undefined);                                      
        }
    }
    else
    {   // TODO : SCOPE is an ARRAY of occurrence scope definitions...
        var l_sc;
        for (l_sc in a_scope) if (a_scope.hasOwnProperty(l_sc))
        {
            var l_scd = a_scope[l_sc];
            switch ( l_sc )
            {
                case Scope.ALL:
                    for (i =0; i< l_occs.length; i++)
                    {
                        addOcc(l_occs[i], l_scd);                                      
                    }
                    break;
                case Scope.CURRENT:
                    if (a_currentPath !== null && a_currentPath[0] && a_currentPath[0].entity === a_realizedEntity)
                    {
                        addOcc(l_occs[a_currentPath[0].dataIdx], l_scd);
                    }
                    break;
                case Scope.FIRST:
                    if (l_occs.length > 0)
                    {
                        addOcc(l_occs[0], l_scd);
                    }
                    break;
                case Scope.LAST:
                    if (l_occs.length > 0)
                    {
                        addOcc(l_occs[l_occs.length-1], l_scd);
                    }
                    break;
                case Scope.MOD:
                    for (i =0; i< l_occs.length; i++)
                    {
                        if (l_occs[i].daoc && l_occs[i].daoc.status==="mod")
                        {
                            addOcc(l_occs[i], l_scd);                                                          
                        }
                    }
                    break;
                case Scope.NEW:
                    for (i =0; i< l_occs.length; i++)
                    {
                        if (l_occs[i].daoc && l_occs[i].daoc.status==="new" )
                        {
                            addOcc(l_occs[i], l_scd);                                                          
                        }
                    }
                    break;
            }
        }
    }
    return l_incOccs;
}

/*
Helper function to resolve entity/field selectors to a list of 
actual entities/fields in scope.
*/
function checkChildInScope(a_realizedField, a_fieldDef,  a_data, a_scope)
{
    var l_scope = { match: false, scope: undefined };
    if (a_scope === undefined)
    {
        // Leaf node. Default (=all) child scope:
        l_scope.match = true;
    }
    else
    {
        var i;
        var l_collectionName;

        // Match, construct child scope 
        if (a_fieldDef.type == "field")
        {
            l_collectionName = "fields";
        }
        else if (a_fieldDef.type == "entity")
        {
            l_collectionName = "entities";
        }
        
        if (a_scope[l_collectionName] === undefined)
        {
            a_scope[l_collectionName] = [];
        }
        if (a_scope[l_collectionName].length > 0)
        {
            UNIFACE.throwException("Error: scope selectors not supported yet.");
        }
        for (i=0; i< a_scope[l_collectionName].length; i++)
        {
            // Try to match, 
            /* 
            match = true
            l_childScope = mergeScope(l_childScope, a_scope[l_collectionName][i]);
            */
        }            	    
    }
    return l_scope;
}

/*
For each node type, (entity, occurrence, field, label, instance) in implementation of a few functions:


serialize: 
    Create a JSON item for the node (and recursively for contained nodes) taking input scope into account
        as a side effect, determine valid currence taking input scope into account (i.e. only set current 
        occurrence to an occurrence that is actually sent to the server)
blockScope : 
    (recursively) Block the node if the output scope requires that.
    Return a structure describing all the blocked nodes.
    The returned structure is attached to the queued/executed request so it can be unblocked later
checkScope : 
    Check recursively check that the node (or any of its children) is not blocked
unblockScope : 
    Unblock any blocked nodes. Input is the data structure produced by blockScope

*/
var nodes = 
{   
    entity : {
        serialize : function (a_realizedEntity,  a_entityDef, a_data, a_scope, a_currentPath, r_ccyInScope )
        {
            var l_json;
            var l_incOccs = findOccsInScope(a_realizedEntity,a_data, a_scope, a_currentPath);

            if (l_incOccs.length>0)
            {
                var i;
                l_json ={ occs: [] };
                for (i =0; i< l_incOccs.length; i++)
                {
                    if ( l_incOccs[i].daoc.status != "empty")
                    {
                        var lP = null;
                        if (a_currentPath != null && l_incOccs[i].stoc === a_currentPath[0]) /* pragma(allow-loose-compare) */
                        {   /* This occ is on the active path */
                            lP = a_currentPath.slice(1); 
                            r_ccyInScope.push(a_currentPath[0]);
                        }
                        l_json.occs.push(nodes.occurrence.serialize(l_incOccs[i].stoc, a_entityDef.occs, l_incOccs[i].daoc, l_incOccs[i].scp, lP, r_ccyInScope ));
                    }
                }
            }
            return l_json;
        },

        blockScope : function(a_realizedEntity, a_entityDef, a_data, a_scope, a_currentPath )
        {
            var l_blocked = { rel : a_realizedEntity, def : a_entityDef, dat : a_data, occs: {}, b:false };
            var l_incOccs = findOccsInScope(a_realizedEntity,a_data, a_scope, a_currentPath);
            
            if (a_scope === undefined)
            {
                l_blocked.b=true;
                a_data.block = (a_data.block ? a_data.block+1 : 1);
            }

            if (l_incOccs.length>0)
            {
                var i;
                for (i =0; i< l_incOccs.length; i++)
                {
                    var lP = null;
                    if (a_currentPath != null && l_incOccs[i].stoc === a_currentPath[0]) /* pragma(allow-loose-compare) */
                    {   /* This occ is on the active path */
                        lP = a_currentPath.slice(1); 
                    }
                    l_blocked.occs[l_incOccs[i].daoc.id] = nodes.occurrence.blockScope(l_incOccs[i].stoc, a_entityDef.occs, l_incOccs[i].daoc, l_incOccs[i].scp, lP );
                }
            }
            return l_blocked;
        },

        checkScope : function(a_realizedEntity, a_entityDef, a_data, a_scope, a_currentPath )
        {
            var l_blocked;
            if (a_data.block)
            {
                return true;
            }
            var l_incOccs = findOccsInScope(a_realizedEntity,a_data, a_scope, a_currentPath);

            if (l_incOccs.length>0)
            {
                var i;
                l_blocked = [];
                for (i =0; i< l_incOccs.length; i++)
                {
                    var lP = null;
                    if (a_currentPath != null && l_incOccs[i].stoc === a_currentPath[0]) /* pragma(allow-loose-compare) */
                    {   /* This occ is on the active path */
                        lP = a_currentPath.slice(1); 
                    }
                    if (nodes.occurrence.checkScope(l_incOccs[i].stoc, a_entityDef.occs, l_incOccs[i].daoc, l_incOccs[i].scp, lP ))
                    {
                        return true;
                    }
                }
            }
            return false;
        },
        unblockScope : function(a_outScope )
        {
            var l_occ;
            a_outScope.dat.block = a_outScope.dat.block ? a_outScope.dat.block-1 : 0;
            for (l_occ in a_outScope.occs) if (a_outScope.occs.hasOwnProperty(l_occ))
            {
                 nodes.occurrence.unblockScope(a_outScope.occs[l_occ]);
            }
        },
        merge : function( a_entityDef, a_oldData, a_newData, a_scope )
        {
            if (a_scope === undefined)
            {        
                return;
            }        
            if (true && a_scope.b)
            {
                // Blocked: overwrite list
                a_oldData.occs = a_newData.occs;
                
            }
            else
            {
                // 1. Build index
                var i;
                var l_oldIdx = {};
                
                if (a_scope === undefined)
                {
                    a_scope = {};
                }
                if (a_scope.occs === undefined)
                {
                    a_scope.occs = {};
                }
                
                for (i = 0; i< a_oldData.occs.length; i++)
                {
                    l_oldIdx[a_oldData.occs[i].id] = a_oldData.occs[i];
                    a_oldData.occs[i].inScope = false;
                    a_oldData.occs[i].dirty = false;
               }
                
                // 2. Mark
                for (i in a_scope.occs) 
                {
                    if (a_scope.occs.hasOwnProperty(i))
                    {
                        if (l_oldIdx[i])
                        {
                            l_oldIdx[i].inScope = a_scope.occs[i].b;
                        }
                    }
                }
                    
                var l_emptyOcc = null;
                // 3. Overlay new
                for (i = 0; i< a_newData.occs.length; i++)
                {

                    if (l_oldIdx[a_newData.occs[i].id] === undefined)
                    {
                        if (a_newData.occs[i].status == "empty")
                        {
                            l_emptyOcc = a_newData.occs[i];
                        }        
                        else
                        {
                            // New occ: add it
                            a_oldData.occs.push(a_newData.occs[i]);
                            a_newData.occs[i].dirty = true;
                        }
                    }
                    else
                    {
                        // Overlay
                        l_oldIdx[a_newData.occs[i].id].inScope = false;
                        l_oldIdx[a_newData.occs[i].id].dirty = true;
                        nodes.occurrence.merge(a_entityDef.occs, l_oldIdx[a_newData.occs[i].id], a_newData.occs[i], a_scope.occs[a_newData.occs[i].id]);
                    }
                }
                
                var l_isEmpty = true;
                // 4. Remove marked occs
                for ( i = a_oldData.occs.length -1; i >=0; i--)
                {
                    if (a_oldData.occs[i].inScope)
                    {
                        a_oldData.occs.splice(i,1);
                    }
                    else
                    {
                        if (a_oldData.occs[i].status != "del")
                        {
                            l_isEmpty = false;
                        }
                    }
                }
                if (l_isEmpty && l_emptyOcc!==null)
                {
                    a_oldData.occs.push(l_emptyOcc);
                }
            }
        }
    },
    occurrence : {
        serialize: function(a_realizedOcc, a_occDef, a_occData, a_scope, a_currentPath, r_ccyInScope )
        {
                var l_serialized = {};
                
                function cpProp(a_prop)
                {
                    if (typeof a_occData[a_prop] != "undefined")
                    {
                        l_serialized[a_prop] = a_occData[a_prop];
                    }

                }
                var l_child;                
                if (a_occData  &&(typeof a_occData != "undefined")) 
                {
                    cpProp("crc");
                    cpProp("uid");
                    cpProp("id");
                    cpProp("valerr");
                }
                
                var l_childName;
                for (l_child in a_occDef) if (a_occDef.hasOwnProperty(l_child))
                {
                    l_childName = a_occDef[l_child].nm;
                    if (typeof a_occDef[l_child].type == "string") /* pragma(allow-loose-compare) */
                    {
                        var l_fldDef = a_occDef[l_child],
                            l_occData = null;
                        
                        if (a_occData && (typeof a_occData[l_child] != "undefined") )
                        {
                            l_occData = a_occData[l_child];
                            var l_occStruct = null;
                            if (a_realizedOcc)
                            {
                                l_occStruct = a_realizedOcc.getChild(l_child);
                            }

                            var l_childScope = checkChildInScope(l_occStruct, a_occDef[l_child], a_occData[l_child], a_scope);
                            if (l_childScope.match)
                            {
                                var l_serializedChild = nodes[l_fldDef.type].serialize(l_occStruct, l_fldDef, l_occData, l_childScope.scope, a_currentPath, r_ccyInScope);
                                if (l_serializedChild !== undefined)
                                {
                                    l_serialized[l_child] = l_serializedChild;
                                }
                            }
                        }
                    }
                }
                if (a_occData.status != undefined) /* pragma(allow-loose-compare) */
                {
                    l_serialized.status =a_occData.status;
                }
                return l_serialized;    
        },

        blockScope : function(a_realizedOcc, a_occDef, a_occData, a_scope, a_currentPath )
        {
                var l_blockList = {};
                
                var l_child;
                var l_blockThis = a_scope === undefined || (a_scope.fields !== undefined && a_scope.fields.length >0 );
            
                if (l_blockThis)
                {
                    a_occData.block = a_occData.block ? a_occData.block+1 : 1;
                    // For Now: 'grey out'
                    if (a_realizedOcc)
                    {  
                        a_realizedOcc.showBlock();
                    }
                }
                
                var l_childName;
                for (l_child in a_occDef) if (a_occDef.hasOwnProperty(l_child))
                {
                    if (typeof a_occDef[l_child].type === "string")
                    {
                        l_childName = a_occDef[l_child].nm;

                        var l_fldDef = a_occDef[l_child],
                            l_occData = null;
                        
                        if (a_occData && (typeof a_occData[l_child] != "undefined") )
                        {
                            l_occData = a_occData[l_child];
                            var l_occStruct = null;
                            if (a_realizedOcc)
                            {
                                l_occStruct = a_realizedOcc.getChild(l_child);
                            }
                            
                            var l_childScope = checkChildInScope(l_occStruct, a_occDef[l_child], a_occData[l_child], a_scope);
                            
                            if (l_childScope.match)
                            {
                            
                                var l_blockedScope = nodes[l_fldDef.type].blockScope(l_occStruct, l_fldDef, l_occData, l_childScope.scope, a_currentPath);
                                if (l_blockedScope !== undefined)
                                {
                                    l_blockList[l_child] = l_blockedScope;
                                }
                            }
                        }
                    }
                }
                return { occ: a_occData, vocc: a_realizedOcc, c: l_blockList, b: (l_blockThis)};    
        },
        /* Scope: array of selector + extra layer
         */        
        checkScope : function(a_realizedOcc, a_occDef, a_occData, a_scope, a_currentPath )
        {
            var l_blockList = {};
            
            var l_child;
            
            // This occurrence itself must be blocked if either:
            //  - the occurrence itself is a leave node in the scope
            //  - we have (some) fields of this occurrence
            var l_blockThis = a_scope === undefined || (a_scope.fields !== undefined && a_scope.fields.length >0 );
            
            if (l_blockThis && a_occData.block)
            {
                return true;
            }
            
            var l_childName;

            for (l_child in a_occDef) if (a_occDef.hasOwnProperty(l_child))
            {
                if (typeof a_occDef[l_child].type === "string")
                {
                	l_childName = a_occDef[l_child].nm;


                    var l_fldDef = a_occDef[l_child],
                        l_occData = null;
                
                    if (a_occData && (typeof a_occData[l_child] != "undefined") )
                    {
                        l_occData = a_occData[l_child];
                        var l_occStruct = null;
                        if (a_realizedOcc)
                        {
                            l_occStruct = a_realizedOcc.getChild(l_child);
                        }
                        
                        var l_childScope = checkChildInScope(l_occStruct, a_occDef[l_child], a_occData[l_child], l_blockThis ? undefined : a_scope);

                        if (l_childScope.match) 
                        {
                            if (nodes[l_fldDef.type].checkScope(l_occStruct,  l_fldDef, l_occData, l_childScope.scope, a_currentPath))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        },

        unblockScope : function(a_outScope)
        {
            
            if (a_outScope.b)
            {
                a_outScope.occ.block = a_outScope.occ.block ? a_outScope.occ.block -1: 0;
                if (    a_outScope.vocc != null) /* pragma(allow-loose-compare) */
                {  
                        a_outScope.vocc.showUnblock();
                }
            }

               
            var l_child;
            for (l_child in a_outScope.c) if (a_outScope.c.hasOwnProperty(l_child))
            {   
                
                nodes[a_outScope.c[l_child].def.type].unblockScope (a_outScope.c[l_child]);
            }
        },
        
        merge: function( a_occDef, a_oldData, a_newData, a_scope  )
        {
            if (a_scope === undefined)
            {        
                return;
            }
            function cpProp(a_prop)
            {
                if (typeof a_newData[a_prop] != "undefined")
                {
                    a_oldData[a_prop] = a_newData[a_prop];
                }

            }
            var l_child;
            
            cpProp("crc");
            cpProp("uid");
            cpProp("id");
            cpProp("valerr");
            cpProp("status");

            for (l_child in a_occDef) if (a_occDef.hasOwnProperty(l_child))
            {
                if (typeof a_occDef[l_child].type == "string")
                {
                    var l_fldDef = a_occDef[l_child];
                    var l_occData = null;
                    
                    var l_newData = a_newData[l_child];
                    var l_oldData = a_oldData[l_child]; /* pragma(allow-loose-compare) */
                    
                    if ( l_newData !== undefined)
                    {
                        if (l_fldDef.type == "entity")
                        {
                            var l_scope;
                            l_scope = a_scope.c[l_child];                    
                            nodes.entity.merge( l_fldDef, l_oldData, l_newData, l_scope);
                        }
                        else if (l_fldDef.type == "field")
                        {
                            UNIFACE.luv.overlayFieldProperties( l_fldDef, l_newData, l_oldData);
                            a_oldData[l_child]=l_newData;
                        }
                    }
                }
            } 
        }
    },
    field : {
        serialize: function(a_realizedField, a_fldDef, a_fldData, a_scope, a_currentPath )
        {
            var lData = a_fldDef,
                JSON = {};
                
            if (a_fldData) {
                lData = a_fldData;
            }
                
            var lVal = null;
                
            if (!a_realizedField || !a_realizedField.isModified() && (!a_realizedField.widget || typeof a_realizedField.widget.valudate !== "function" || !a_realizedField.widget.validate())) { 
                lVal = lData.value;
            } else {
                lVal = a_realizedField.checkedValue();
                if ( lVal.error && (!a_realizedField.mergedProperties.uniface || typeof a_realizedField.mergedProperties.uniface.clientsyntaxcheck !== "string" || a_realizedField.mergedProperties.uniface.clientsyntaxcheck.match(/^(on|yes|y|true|t|1)$/i))) //pragma(allow-loose-compare)
                {
                    a_realizedField.showError(lVal.error);
                    UNIFACE.throwException(lVal.error, false);
                }
                lVal = lVal.normalizedValue;
            }

            if (lData.hash)
            {
                JSON.hash = lData.hash;
            }
             
            JSON.value = lVal;

            return JSON;        
        },
        blockScope : function(a_realizedField, a_fldDef, a_fldData, a_scope, a_currentPath )
        {
            if (a_realizedField != null && !a_fldData.block ) // pragma(allow-loose-compare)
            {
                a_realizedField.showBlock();
            } 
            a_fldData.block = a_fldData.block ? a_fldData.block+1 : 1;
            return { rel : a_realizedField, def : a_fldDef, dat : a_fldData, b:true };
        },
        checkScope : function(a_realizedField, a_fldDef, a_fldData, a_scope, a_currentPath )
        {
        },
        unblockScope : function(a_outScope )
        {
            a_outScope.dat.block = a_outScope.dat.block ? a_outScope.dat.block-1 : 0;
            UNIFACE.trace(a_outScope.def.nm + " block count: " + a_outScope.dat.block);

            if (a_outScope.rel != null ) // pragma(allow-loose-compare)
            {
                a_outScope.rel.showUnblock();          
            }
        }
    },
    label : {
        serialize: nop,
        blockScope : nop,
        checkScope : nop,
        unblockScope : nop
    },
    instance : {
        serialize: function(a_instance, a_compDef, a_instData, a_scope, a_currentPath, r_ccyInScope )
        {
            var l_serialized = {};
            if (a_instance.getStructureMap()[0])
            {
                a_instance.getStructureMap()[0].UpdateStatus(a_instData);
                l_serialized[a_instance.instanceName]=nodes.occurrence.serialize(a_instance.getStructureMap()[0], a_compDef, a_instData,  a_scope.input, a_currentPath, r_ccyInScope);
                l_serialized[a_instance.instanceName].scope = a_scope.scopeName;
                return { "newdata" : l_serialized };        
            }
            else
            {
                return undefined;
            }
        },
        blockScope : function(a_instance, a_compDef, a_instData, a_scope, a_currentPath )
        {
            if (a_instance.getStructureMap()[0])
            {
                a_instance.getStructureMap()[0].UpdateStatus(a_instData);
                return  nodes.occurrence.blockScope(a_instance.getStructureMap()[0], a_compDef, a_instData,  a_scope.output, a_currentPath);
            }
            else
            {
                this.block += 1;
                return undefined;
            }
                
        },
        checkScope : function(a_instance, a_compDef, a_instData, a_scope, a_currentPath )
        {
            if (a_instance.block > 0)
            {
                // Instance blocked: no  dice
                return true;
            }
            else if (a_instance.getStructureMap()[0])
            {
                // Loaded -> check scope
                a_instance.getStructureMap()[0].UpdateStatus(a_instData);
                return nodes.occurrence.checkScope(a_instance.getStructureMap()[0], a_compDef, a_instData, mergeScope(a_scope.input, a_scope.output), a_currentPath);
            }
            else
            {
                // Not loaded yet, no input expected -> the request is allowed
                return !isEmptyScope(a_scope.input);
            }
        },
        unblockScope : function(a_outScope) {
            if (a_outScope !==  undefined)
            {
                nodes.occurrence.unblockScope(a_outScope);
            }
            this.block = this.block ? this.block-1 : 0;
            UNIFACE.trace("block count: " + this.block);
        }
    }
}; // nodes

/** 
@short Constructor for RequestScope object

The reuquest scope object encapsulates a request with its context and scope.
It can be queues (e.g. put on the blockedCommands list) or executed dirtectrly.
*/
function RequestScope()
{
    this.scope = undefined;
    this.outscope=undefined;
    this.id = UNIFACE.dl.requestManager.generateId();
}

RequestScope.prototype = {
initByDef : function() {
    this.definition = this.realizedObject.definition;
    
    var l_moduleList={exec: {requesttype : "update", input:[]}};
    
    if (this.definition)
    {
        l_moduleList = this.moduleType == "t" ? this.definition.triggers : this.definition.operations;
    }
    else if ( typeof this.moduleName  === "string" && this.moduleName  !== "exec" )
    {
    	l_moduleList[this.moduleName] = {requesttype : "delayed", input:[]};
    	this.instance.delayedScope = this;
    }
    
    if (l_moduleList!==undefined )
    {
        this.scope = l_moduleList[this.moduleName ];
    } 
    
    this.defName = "";
    if (this.definition && this.definition.nm)
    {
        this.defName = this.definition.nm;
    }
},

create: function(a_moduleName, a_realizedObject, a_args, a_moduleType)
{
    this.args = a_args;
    this.moduleName = a_moduleName.toLowerCase();
    this.moduleType = a_moduleType.toLowerCase();
    this.instance = a_realizedObject.instance ? a_realizedObject.instance : a_realizedObject;
    this.realizedObject = a_realizedObject;
    this.data = a_realizedObject.data;
    this.initByDef();
    
},

unBlock: function()
{
    var i;
    UNIFACE.trace("unblocking " + this.instance.instanceName);
    if ( this.instance.delayedScope === this && this.instance.definition ) {
     	this.initByDef();
    	this.instance.delayedScope = undefined;
    }
    for (i in this.outScope) if (this.outScope.hasOwnProperty(i) && this.outScope[i]!==undefined)
    {
        UNIFACE.trace("unblocking instance...." + this.outScope[i].vocc.instance.instanceName);
    
        nodes.instance.unblockScope(this.outScope[i]);
    }
},

/*
Iterate the structure created by the traverseScopes() function.
call a function for each instance + associated input/ouput scope of the instance
Used by 
*/
visitScopeInstances : function(a_fn, r_ccyInScope)
{
    var i;
    for (i in this.compScopes) if (this.compScopes.hasOwnProperty(i))
    {
        // Get the instance
        var l_inst = UNIFACE.components[i];
        if (l_inst === undefined)
        {
            UNIFACE.throwException("Component instance " + i + " does not exist!");
        }
        var l_path = undefined;
        var l_ccyInScope = undefined;
        if (i == this.instance.instanceName)
        {
            l_path = this.currency;
            l_ccyInScope = r_ccyInScope;
        }
     
        var l_res = a_fn(l_inst, l_inst.definition, l_inst.data, this.compScopes[i], l_path, l_ccyInScope);
        if (!!!l_res)
        {
            return;
        }
    }
},

validCcy: function(aJsonData)
{
	var ccy = [];
    this.visitScopeInstances(function(a_inst){
                                aJsonData[a_inst.instanceName] = UNIFACE.scope.nodes.instance.serialize.apply(this, arguments);
                                return true;
                             }, ccy );
    return ccy;
},

checkAndBlock: function()
{
    var l_blocked = false;
    var me = this;
    if ( this.scope)
    {
    	if ( this.scope.requesttype === "delayed" )
    	{
    		l_blocked = true;
    	}
    	else 
    	{
	        this.compScopes = traverseScopes(this.instance.instanceName, this.scope);
	        this.compScopes[this.instance.instanceName].scopeName = this.moduleType + " " + this.moduleName + this.componentName ? "." + this.componentName : "";
	        
	        this.currency = null;
	        if (this.realizedObject.getCurrency)
	        {
	            this.currency = this.realizedObject.getCurrency();
	        }
	
	        // 1. Check
	        l_blocked =  (this.realizedObject.data && this.realizedObject.data.block) ;
	        if (!l_blocked) 
	        {
	            this.visitScopeInstances(function(){
	            	l_blocked = l_blocked || nodes.instance.checkScope.apply(this, arguments); 
	            	return !l_blocked; 
	            });
	        }
	        
	        // 2. Block (again)
	        this.outScope = {};
	
	        this.visitScopeInstances(function(a_inst){
	        	me.outScope[a_inst.instanceName] = nodes.instance.blockScope.apply(this, arguments); 
	        	return true;
	        });
    	}
    }
       
    return l_blocked;
},

checkInstance: function()
{
    // Check if realized instance still attached to  luv && pointing to same data
    return (this.data === this.realizedObject.data);
},

execute: function()
{
	//UNIFACE.trace("executeScope: fieldName=" + this.defName);
    // 1. Collect data
    var url;
    var jsonData;
    
    if (this.scope === undefined)
    {
        return;
    }
    
    var l_triggerType = "UPDATE";
    if (this.scope.requesttype !== undefined)
    {
        l_triggerType =  this.scope.requesttype.toUpperCase();
    }
    if (!UNIFACE.luv.util.isValidRequestType(l_triggerType))
    {
        return;
    }
    
    // 2. Execute the trigger
    
    // 2.1. Determine the URL
    url = document.location.href;
    url = url.substring(0, url.lastIndexOf('/')+1) ; // "/uniface/wrd/run", or empty string if no "/"
    url += this.instance.componentName;

    // 2.2. Create the request.
    var l_req = UNIFACE.dl.createRequest(l_triggerType);
    l_req.instance = this.instance;
    l_req.scope = this;
    l_req.id = this.id;

    // 2.3. Validate the data.
    //      This intentionally happens *after* creating the request,
    //      because validation may result in an exception.
    //      This exception is caught here, after which the
    //      the unBlock method of the request is called,
    //      to prevent that data will stay blocked.
    UNIFACE.extension.errorWidget.hide();
    jsonData = {};
    var l_validCcy;
    try {
        l_validCcy = this.validCcy(jsonData);
    } catch (e) {
        // Unexpected exception here.
        // We're will break off this whole request.
        // Therefore we must unblock first.
        UNIFACE.dl.initialCommand( { unblock : l_req.instance}, l_req);
        UNIFACE.dl.commands.run();    
        // ... and then discontinue the current request:
        // return undefined;
        return;
    }
 
    // 2.4. Add all kinds of attributes/parameters to the request.   
    var l_in;
    for (l_in in jsonData) if (jsonData.hasOwnProperty(l_in))
    {
        if (jsonData[l_in] !== undefined)
        {
            l_req.addBodyPart("data:" + l_in.toUpperCase(), JSON.stringify(jsonData[l_in]));
        }
    }
    l_req.setURL(url);
    if (this.moduleName !== "exec")
    {
        l_req.setOperation( this.moduleName, true);
        l_req.addParam("u:moduletype", this.moduleType);
    }
    
    if (this.defName)
    {
        l_req.addParam("u:fieldname", this.defName);
    }
    
    for (var i = 0; i < l_validCcy.length; i++) 
    {
        if (l_validCcy[i].status!=="empty")
        {
            l_req.addParam("u:entity." + this.instance.instanceName + "." +i, l_validCcy[i].entity.entDef.nm);
            l_req.addParam("u:occid."+ this.instance.instanceName + "." +i, l_validCcy[i].occID);
        }
    }
    for ( var param in this.args ) if ( this.args.hasOwnProperty(param) ) {
    	l_req.addParam(param, this.args[param]);
    }    
    if ( this.instance.componentName && this.instance.instanceName != this.instance.componentName ) 
    {
    	l_req.addParam("u:insname", this.instance.instanceName ); 
    }
    if (this.args["u:dspmode"] === "embed")
    {
        l_req.requestType =  "static";
        l_req.responseType =  "update";
    }    
    l_req.send();
}   

};


UNIFACE.namespace("scope").extend(
{
	nodes : nodes,
	//traverseScopes : traverseScopes
	RequestScope : RequestScope
});

})();
