/// <reference path="jquery-1.6.2.js" />
(function ($, window) {
    /// <param name="$" type="jQuery" />
    "use strict";

    if (typeof ($.signalR) !== "function") {
        throw "SignalR: SignalR is not loaded. Please ensure jquery.signalR.js is referenced before ~/signalr/hubs.";
    }

    var hubs = {},
        signalR = $.signalR,
        callbackId = 0,
        callbacks = {};

    // Array.prototype.map
    if (!Array.prototype.hasOwnProperty('map')) {
        Array.prototype.map = function (fun, thisp) {
            var arr = this,
                i,
                length = arr.length,
                result = [];
            for (i = 0; i < length; i += 1) {
                if (arr.hasOwnProperty(i)) {
                    result[i] = fun.call(thisp, arr[i], i, arr);
                }
            }
            return result;
        };
    }

    function executeCallback(hubName, fn, args, state) {
        var hub = hubs[hubName],
            method;

        if (hub) {
            signalR.hub.processState(hubName, hub.obj, state);

            method = hub[fn];
            if (method) {
                method.apply(hub.obj, args);
            }
        }
    }

    function updateClientMembers(instance) {
        var newHubs = {},
            obj,
            hubName = "",
            newHub,
            memberValue,
            key,
            memberKey;

        for (key in instance) {
            if (instance.hasOwnProperty(key)) {

                obj = instance[key];

                if ($.type(obj) !== "object" ||
                        key === "prototype" ||
                        key === "constructor" ||
                        key === "fn" ||
                        key === "hub" ||
                        key === "transports") {
                    continue;
                }

                newHub = null;
                hubName = obj._.hubName;

                for (memberKey in obj) {
                    if (obj.hasOwnProperty(memberKey)) {
                        memberValue = obj[memberKey];

                        if (memberKey === "_" ||
                                $.type(memberValue) !== "function" ||
                                $.inArray(memberKey, obj._.ignoreMembers) >= 0) {
                            continue;
                        }

                        if (!newHub) {
                            newHub = { obj: obj };

                            newHubs[hubName] = newHub;
                        }

                        newHub[memberKey] = memberValue;
                    }
                }
            }
        }

        hubs = {};
        $.extend(hubs, newHubs);
    }

    function getArgValue(a) {
        return $.isFunction(a) ? null :
            ($.type(a) === "undefined"
                ? null : a);
    }

    function copy(obj, exclude) {
        var newObj = {};
        $.each(obj, function (key, value) {
            if ($.inArray(key, exclude) === -1) {
                // We don't use "this" because browser suck!
                newObj[key] = value;
            }
        });

        return newObj;
    }

    function serverCall(hub, methodName, args) {
        var callback = args[args.length - 1], // last argument
            methodArgs = $.type(callback) === "function"
                ? args.slice(0, -1) // all but last
                : args,
            argValues = methodArgs.map(getArgValue),
            data = { hub: hub._.hubName, action: methodName, data: argValues, state: copy(hub, ["_"]), id: callbackId },
            d = $.Deferred(),
            cb = function (result) {
                signalR.hub.processState(hub._.hubName, hub, result.State);

                if (result.Error) {
                    d.rejectWith(hub, [result.Error]);
                } else {
                    if ($.type(callback) === "function") {
                        callback.call(hub, result.Result);
                    }
                    d.resolveWith(hub, [result.Result]);
                }
            };

        callbacks[callbackId.toString()] = { scope: hub, callback: cb };
        callbackId += 1;
        hub._.connection().send(window.JSON.stringify(data));
        return d;
    }

    // Create hub signalR instance
    $.extend(signalR, {
        chat: {
            _: {
                hubName: 'SignalR.Samples.Hubs.Chat.Chat',
                ignoreMembers: ['join', 'getConnectionCR', 'getConnection', 'changeNotification', 'send', 'joinChat', 'setVipMode', 'removeUser', 'disconnect', 'getUsers', 'getRecentMessages', 'removeMessage', 'editMessage', 'lockRoom', 'unlockRoom', 'roomStatus', 'getVPUserRoom', 'executequery', 'getUserRoom', 'typing', 'requestCurrentPresenters', 'startUser', 'customMessage', 'sendCurrentPresenters', 'tokBoxId', 'unjoinSoapbox', 'startPresentations', 'stopPresentations', 'requestTBToken', 'nextVideo', 'likeBroadcast', 'dislikeBroadcast', 'updateVotes', 'getChatUser', 'dbRooms', 'dbUsers', 'changeName', 'changeUserDetails', 'deleteUser', 'changeRoomDetails', 'updateMediaString', 'updateAvatar', 'getAvatars', 'getTemplates', 'updateNewRoomName', 'roomDetails', 'createRoom', 'namespace', 'ignoreMembers', 'callbacks'],
                connection: function () { return signalR.hub; }
            }
,
            join: function (callback) {
                return serverCall(this, "Join", $.makeArray(arguments));
            },

            getConnectionCR: function (password, callback) {
                return serverCall(this, "GetConnectionCR", $.makeArray(arguments));
            },

            getConnection: function (password, callback) {
                return serverCall(this, "GetConnection", $.makeArray(arguments));
            },

            changeNotification: function (value, callback) {
                return serverCall(this, "ChangeNotification", $.makeArray(arguments));
            },

            send: function (content, callback) {
                return serverCall(this, "Send", $.makeArray(arguments));
            },

            joinChat: function (content, callback) {
                return serverCall(this, "JoinChat", $.makeArray(arguments));
            },

            setVipMode: function (user, vipMode, callback) {
                return serverCall(this, "SetVipMode", $.makeArray(arguments));
            },

            removeUser: function (id, callback) {
                return serverCall(this, "RemoveUser", $.makeArray(arguments));
            },

            disconnect: function (callback) {
                return serverCall(this, "Disconnect", $.makeArray(arguments));
            },

            getUsers: function (callback) {
                return serverCall(this, "GetUsers", $.makeArray(arguments));
            },

            getRecentMessages: function (callback) {
                return serverCall(this, "GetRecentMessages", $.makeArray(arguments));
            },

            removeMessage: function (msgId, callback) {
                return serverCall(this, "RemoveMessage", $.makeArray(arguments));
            },

            editMessage: function (msgId, newMsg, callback) {
                return serverCall(this, "EditMessage", $.makeArray(arguments));
            },

            lockRoom: function (message, callback) {
                return serverCall(this, "LockRoom", $.makeArray(arguments));
            },

            unlockRoom: function (callback) {
                return serverCall(this, "UnlockRoom", $.makeArray(arguments));
            },

            roomStatus: function (callback) {
                return serverCall(this, "RoomStatus", $.makeArray(arguments));
            },

            getVPUserRoom: function (user, callback) {
                return serverCall(this, "GetVPUserRoom", $.makeArray(arguments));
            },

            executequery: function (command, callback) {
                return serverCall(this, "Executequery", $.makeArray(arguments));
            },

            getUserRoom: function (user, callback) {
                return serverCall(this, "GetUserRoom", $.makeArray(arguments));
            },

            typing: function (isTyping, callback) {
                return serverCall(this, "Typing", $.makeArray(arguments));
            },

            requestCurrentPresenters: function (callback) {
                return serverCall(this, "RequestCurrentPresenters", $.makeArray(arguments));
            },

            startUser: function (param, callback) {
                return serverCall(this, "StartUser", $.makeArray(arguments));
            },

            customMessage: function (room, header, message, callback) {
                return serverCall(this, "CustomMessage", $.makeArray(arguments));
            },

            sendCurrentPresenters: function (sq, users, votes, time, callback) {
                return serverCall(this, "SendCurrentPresenters", $.makeArray(arguments));
            },

            tokBoxId: function (id, callback) {
                return serverCall(this, "TokBoxId", $.makeArray(arguments));
            },

            unjoinSoapbox: function (callback) {
                return serverCall(this, "UnjoinSoapbox", $.makeArray(arguments));
            },

            startPresentations: function (callback) {
                return serverCall(this, "StartPresentations", $.makeArray(arguments));
            },

            stopPresentations: function (callback) {
                return serverCall(this, "StopPresentations", $.makeArray(arguments));
            },

            requestTBToken: function (url, callback) {
                return serverCall(this, "RequestTBToken", $.makeArray(arguments));
            },

            nextVideo: function (callback) {
                return serverCall(this, "NextVideo", $.makeArray(arguments));
            },

            likeBroadcast: function (callback) {
                return serverCall(this, "LikeBroadcast", $.makeArray(arguments));
            },

            dislikeBroadcast: function (callback) {
                return serverCall(this, "DislikeBroadcast", $.makeArray(arguments));
            },

            updateVotes: function (q, status, callback) {
                return serverCall(this, "UpdateVotes", $.makeArray(arguments));
            },

            getChatUser: function (id, callback) {
                return serverCall(this, "GetChatUser", $.makeArray(arguments));
            },

            dbRooms: function (callback) {
                return serverCall(this, "DbRooms", $.makeArray(arguments));
            },

            dbUsers: function (callback) {
                return serverCall(this, "DbUsers", $.makeArray(arguments));
            },

            changeName: function (newname, callback) {
                return serverCall(this, "ChangeName", $.makeArray(arguments));
            },

            changeUserDetails: function (id, userID, userName, userAvatar, moderator, vip, gender, caller, callback) {
                return serverCall(this, "ChangeUserDetails", $.makeArray(arguments));
            },

            deleteUser: function (userid, caller, callback) {
                return serverCall(this, "DeleteUser", $.makeArray(arguments));
            },

            changeRoomDetails: function (id, roomID, label, mediastring, mediaalt, template, mediascreen, panelvideo, chat, caller, callback) {
                return serverCall(this, "ChangeRoomDetails", $.makeArray(arguments));
            },

            updateMediaString: function (mediaStr, altTag, callback) {
                return serverCall(this, "UpdateMediaString", $.makeArray(arguments));
            },

            updateAvatar: function (avatar, callback) {
                return serverCall(this, "UpdateAvatar", $.makeArray(arguments));
            },

            getAvatars: function (gender, callback) {
                return serverCall(this, "GetAvatars", $.makeArray(arguments));
            },

            getTemplates: function (callback) {
                return serverCall(this, "GetTemplates", $.makeArray(arguments));
            },

            updateNewRoomName: function (oldname, newname, callback) {
                return serverCall(this, "UpdateNewRoomName", $.makeArray(arguments));
            },

            roomDetails: function (roomid, callback) {
                return serverCall(this, "RoomDetails", $.makeArray(arguments));
            },

            createRoom: function (id, label, mediaString, mediaAlt, template, mediaScreen, panelVideo, chat, caller, callback) {
                return serverCall(this, "CreateRoom", $.makeArray(arguments));
            }
        }
    });

    signalR.hub = signalR("/signalr")
        .starting(function () {
            updateClientMembers(signalR);
        })
        .sending(function () {
            var localHubs = [];

            $.each(hubs, function (key) {
                var methods = [];

                $.each(this, function (key) {
                    if (key === "obj") {
                        return true;
                    }

                    methods.push(key);
                });

                localHubs.push({ name: key, methods: methods });
            });

            this.data = window.JSON.stringify(localHubs);
        })
        .received(function (result) {
            if (result) {
                if (!result.Id) {
                    executeCallback(result.Hub, result.Method, result.Args, result.State);
                } else {
                    var callback = callbacks[result.Id.toString()];
                    if (callback) {
                        callback.callback.call(callback.scope, result);
                    }
                }
            }
        });

    signalR.hub.processState = function (hubName, left, right) {
        $.extend(left, right);
    };

}(window.jQuery, window));
