/*Widget Framework 2.0*/
if ('undefined' == typeof (MsnVideo2) || null == MsnVideo2)
{
    MsnVideo2 = new function ()
    {
        var messageListeners = {};     //array of message listeners
        var propertyProviders = {};    //array of property providers
        var nextWidgetId = 1;

        //the listener datatype
        function listener(widgetId, widgetGroup, listenerFunc)
        {
            //normalize empty strings to null
            this.widgetId = normalizeString(widgetId);
            this.widgetGroup = normalizeString(widgetGroup);
            this.listenerFunc = listenerFunc;
        }

        //calls the js and rich widget listeners
        function safeListenerCall(listenerItem, msg)
        {
            var eventType = msg.type;
            var sourceId = msg.sourceId;
            var sourceGroup = msg.sourceGroup;
            var targetId = msg.targetId;
            var targetGroup = msg.targetGroup;
            var param = msg.param;

            if (!checkFunction(listenerItem.listenerFunc)) //rich widget function
            {
                var node = getRichWidgetNodeById(listenerItem.widgetId);

                if (checkDefined(node))
                {
                    //Call flash external interface function
                    if (checkFunction(node.MsnVideoCallback))
                    {
                        node.MsnVideoCallback(eventType, param, sourceId, targetId);
                    }
                    //Call silverlight bridge
                    else if (node.Content && node.Content.JSCommunicator)
                    {
                        var convertedMsg = createSLObject(node, "Message", msg);

                        node.Content.JSCommunicator.ReceiveMessageJS(convertedMsg);
                    }
                }
            }
            else //Call javascript listener
            {
                listenerItem.listenerFunc(msg);
            }
        }

        //Converts a javascript object to silverlight
        function createSLObject(slNode, className, jsonObj)
        {
            var obj;
            //already a sl object
            if ("unknown" == typeof (jsonObj.hasOwnProperty))
            {
                obj = jsonObj;
            }
            else
            {
                obj = slNode.Content.services.createObject(className);
                for (var prop in jsonObj)
                {
                    if ("unknown" != typeof (obj[prop]))
                    {
                        obj[prop] = jsonObj[prop];
                    }
                }
            }
            return obj;
        }

        function getRichWidget(id, nodes)
        {
            var richWidget = null;

            var len = nodes.length;
            for (var i = 0; i < len; i++)
            {
                try
                {
                    //We don't check nodes[i].Content.JScommunicator.GetWidgetId, because it will throw a "Invalid InvokeType"
                    if ((checkFunction(nodes[i].GetWidgetId) && id == nodes[i].GetWidgetId()) // flash
                        || (nodes[i].Content && nodes[i].Content.JSCommunicator && id == nodes[i].Content.JSCommunicator.GetWidgetId())) // SL
                    {
                        richWidget = nodes[i];
                        break;
                    }
                }
                catch (e)
                {
                    var exception = "caught exception in MsnVideo2.getRichWidget: " + e.description;
                    // this.log(exception); FIXME
                }
            }

            return richWidget;
        }

        //gets the rich widget node by widgetId
        function getRichWidgetNodeById(id)
        {
            var nodes = document.getElementsByTagName("OBJECT");
            var richWidget = getRichWidget(id, nodes);

            if (null == richWidget)
            {
                nodes = document.getElementsByTagName("EMBED");
                richWidget = getRichWidget(id, nodes);
            }

            return richWidget;
        }

        //used to generate a id for rich widgets, since sometimes embed code
        //does not always have id specified
        this.generateWidgetId = function ()
        {
            return "" + nextWidgetId++;
        }

        //handles adding listeners to data structures for properties and messages
        function addHandler(eventType, listeners, listenerItem)
        {
            eventType = eventType.toLowerCase();

            //Creates the widget listener collection for event type
            if (null == listeners[eventType])
            {
                listeners[eventType] = new Array();
            }

            //Looks to see if the widget is already in the collection
            var match = false;
            var arr = listeners[eventType];
            var len = arr.length;

            for (var i = 0; i < len; i++)
            {
                if (arr[i].widgetId == listenerItem.widgetId && arr[i].widgetGroup == listenerItem.widgetGroup)
                {
                    match = true;
                    break;
                }
            }

            //Adds the widget to the collection if not found
            if (!match)
            {
                arr.push(listenerItem);
            }
        }

        function removeHandler(eventType, listeners, widgetId, widgetGroup)
        {
            eventType = eventType.toLowerCase();

            if (checkDefined(listeners[eventType]))
            {
                //remove the widget from the collection
                var arr = listeners[eventType];
                var len = arr.length;

                for (var i = 0; i < len; i++)
                {
                    if (arr[i].widgetId == widgetId && arr[i].widgetGroup == widgetGroup)
                    {
                        delete arr[i];
                        arr[i] = null;
                        arr.splice(i,1)
                        break;
                    }
                }
            }
        }

        //adds message listeneners
        //eventType, widgetId, widgetGroup, funcCb
        this.addMessageReceiver = function (receiver)
        {
            addHandler(receiver.eventType, messageListeners, new listener(receiver.widgetId, receiver.widgetGroup, receiver.funcCb));
        };

        //removes message listeners
        this.removeMessageReceiver = function (receiver)
        {
            removeHandler(receiver.eventType, messageListeners, receiver.widgetId, receiver.widgetGroup);
        }

        //Sends a message in js and rich widgets
        this.sendMessage = function (msg)
        {
            var eventType = msg.type.toLowerCase();

            if (checkDefined(messageListeners[eventType]))
            {
                var sourceGroup = normalizeString(msg.sourceGroup);
                var targetId = normalizeString(msg.targetId);
                var targetGroup = normalizeString(msg.targetGroup);

                //If the param is coming from Silverlight, we take the json notation in order
                //to convert it to a actual JS object
                if (checkDefined(msg.paramJson))
                {
                    var slMsg = msg;
                    var paramJson = eval("(" + msg.paramJson + ")");
                    msg =
                    {
                        type: slMsg.type,
                        sourceId: slMsg.sourceId,
                        sourceGroup: slMsg.sourceGroup,
                        targetId: slMsg.targetId,
                        targetGroup: slMsg.targetGroup,
                        param: paramJson
                    };
                }

                var arr = messageListeners[eventType];
                var len = arr.length;

                for (var n = 0; n < len; n++)
                {
                    // if one of the listeners fails, we want to continue to the next one
                    try
                    {
                        var listenerObj = arr[n];

                        //targetId: null means all widgets
                        //targetGroup: has to be exact match. null means default group (sourceGroup), not all groups
                        if ((null == targetId || listenerObj.widgetId == targetId) &&
                            listenerObj.widgetGroup == (null == targetGroup ? sourceGroup : targetGroup))
                        {
                            safeListenerCall(listenerObj, msg);
                        }
                    }
                    catch (e)
                    {
                        //var exception = "caught exception in MsnVideo2.sendMessage: " + e.description;
                        // this.log(exception); FIXME
                    }
                }
            }
        };

        //propertyType, widgetId, widgetGroup, funcCb
        this.addPropertyProvider = function (propProvider)
        {
            addHandler(propProvider.propertyType, propertyProviders, new listener(propProvider.widgetId, propProvider.widgetGroup, propProvider.funcCb));
        }

        //Gets a list of property objects
        this.getProperties = function (propReq)
        {
            var properties = new Array();
            propertyType = propReq.type.toLowerCase();

            if (null != propertyProviders[propertyType])
            {
                //normalizes the strings
                var targetId = normalizeString(propReq.targetId);
                var targetGroup = normalizeString(propReq.targetGroup);
                var sourceGroup = normalizeString(propReq.sourceGroup);

                var arr = propertyProviders[propertyType];
                var len = arr.length;

                for (var n = 0; n < len; n++)
                {
                    // if one of the property providers fails, we want to continue to the next one
                    try
                    {
                        var providerObj = arr[n];

                        //targetId: null means all widgets
                        //targetGroup: has to be exact match. null means default group (sourceGroup), not all groups
                        if ((providerObj.widgetId == targetId || null == targetId) &&
                            providerObj.widgetGroup == (null == targetGroup ? sourceGroup : targetGroup))
                        {
                            //rich widget provider
                            if (!checkFunction(providerObj.listenerFunc))
                            {
                                var node = getRichWidgetNodeById(providerObj.widgetId);
                                var props = null;

                                if (checkDefined(node))
                                {
                                    //Silverlight
                                    if (node.Content && node.Content.JSCommunicator)
                                    {
                                        var convertedPropReq = createSLObject(node, "PropertyRequest", propReq);
                                        props = node.Content.JSCommunicator.HandlePropertyRequestJS(convertedPropReq);
                                    }
                                    //Flash
                                    else if (checkFunction(node.MsnVideoPropertyCallback))
                                    {
                                        props = node.MsnVideoPropertyCallback(propReq);
                                    }

                                    for (var i = 0; null != props && i < props.length; i++)
                                    {
                                        var prop = props[i];

                                        //If the param is coming from Silverlight, we take the json notation in order
                                        //to convert it to a actual JS object
                                        if (checkDefined(prop.paramJson))
                                        {
                                            prop =
                                            {
                                                widgetId: prop.widgetId,
                                                widgetGroup: prop.widgetGroup,
                                                param: eval("(" + prop.paramJson + ")")
                                            };
                                        }

                                        properties.push(prop);
                                    }
                                }
                            }
                            else //Js provider
                            {
                                var prop = providerObj.listenerFunc();
                                properties.push(prop);
                            }
                        }
                    }
                    catch (e)
                    {
                        var exception = "caught exception in MsnVideo2.getProperties: " + e.description;
                        // this.log(exception); FIXME
                    }
                }
            }
            return properties;
        }

        //utility functions

        //normalize empty text to null
        function normalizeString(str) { return checkString(str) ? str : null; }

        function checkFunction(f) { return typeof (f) == "function"; }
        function checkDefined(o) { return typeof (o) != "undefined" && null != o; }
        function checkString(s) { return typeof (s) == "string" && s.length > 0; }
    }

    //We can't trust partners to properly wire up callbacks that recursively call each other,
    //So we'll use this to simulate it
    if ("function" == typeof (MsnVideoInitializeInternal))
    {
        MsnVideoInitializeInternal();
    }

    //This is part of the Widget Framework contract, so consumers know when they 
    //can start calling functions on the widget framework
    if ("function" == typeof (MsnVideoInitialize2))
    {
        MsnVideoInitialize2();
    }
}

