if (!window.CQ_Analytics) {
    window.CQ_Analytics = {};
}

// TODO is there a better way?
if (typeof CQ_Analytics.TestTarget !== 'undefined') {
    var oldTandT = CQ_Analytics.TestTarget;
}

CQ_Analytics.TestTarget = new function() {


    /**
     * Extracts the possible mbox parameters from an object, by looking for entry similar to
     *
     * {
     *   'name'  : 'mbox'
     *   'value' : '...'
     * }
     *
     * @param {Object} the object to inspect for mbox parameters
     */
    function extractMboxParameters(obj, parent) {

        if (!obj) {
            return;
        }

        // see http://stackoverflow.com/a/120280/112671, we want to skip traversal of DOM elements
        if ('nodeType' in obj) {
            return;
        }

        var key, value, parentKey, parentEntry;

        if (obj.hasOwnProperty('name') && obj.hasOwnProperty('value')) {
            for (parentItem in parent) {
                var parentEntry = parent[parentItem];
                if (typeof parentEntry === 'object') {
                    if (parentEntry.hasOwnProperty('name') && parentEntry['name'] == 'mbox') {
                        return parent;
                    }
                }
            }
            return parent;
        }

        for (key in obj) {
            if (!obj.hasOwnProperty(key)) {
                continue;
            }

            var newObj = obj[key];
            if (typeof newObj === 'object') {
                var extracted = extractMboxParameters(newObj, obj);
                if (extracted) {
                    return extracted;
                }
            }
        }

    }

    function isEditOrPreview() {
        var $COOKIE = (document.cookie || '').split(/;\s*/).reduce(function(re, c) {
            var tmp = c.match(/([^=]+)=(.*)/);
            if (tmp) re[tmp[1]] = unescape(tmp[2]);
            return re;
        }, {});

        return (typeof $COOKIE["wcmmode"] == "undefined"
                    || $COOKIE["wcmmode"] == "preview"
                    || $COOKIE["wcmmode"] == "edit");
    }

    function getCookieObject() {
        var $COOKIE = (document.cookie || '').split(/;\s*/).reduce(function(re, c) {
            var tmp = c.match(/([^=]+)=(.*)/);
            if (tmp) re[tmp[1]] = unescape(tmp[2]);
            return re;
        }, {});

        return $COOKIE;
    }

    function pullContent(path) {
        // if possible, force WCM mode as disabled to prevent edit decorations from being applied
        var wcmmode = CQ.shared.HTTP.getParameter(document.location.href, 'wcmmode');
        if (typeof CQ.WCM !== 'undefined') {
            wcmmode = "disabled";
        }
        if (wcmmode && wcmmode.length > 0) {
            path = CQ.shared.HTTP.addParameter(path, 'wcmmode', wcmmode);
        }

        // pull in the content
        var output = CQ.shared.HTTP.get(path);
        var isOk = (output && output.status && output.status == 200);
        var hasBody = (output && output.body && output.body.length > 0);

        if (isOk && hasBody) {
            return output;
        } else {
            if (console) console.log("Could not pull resource. Response[status:{},body:{}]", output.status, output.body);
            return null;
        }
    }

    return {

        lateMboxArrivalTimeouts: {},

        /**
         * Initialises the Adobe Target integration
         *
         * It reads the clientcode from either the clientcode parameter (deprecated) or from
         * the CQ_Analytics.TestTarget.clientCode variable (preferred).
         *
         * @param {String} clientcode the optional client code
         */
        init: function(clientcode) {

            if (CQ_Analytics.TestTarget.clientCode) {
                clientcode = CQ_Analytics.TestTarget.clientCode;
            } else {
                CQ_Analytics.TestTarget.clientCode = clientcode;
            }

            if (clientcode) {

                CQ_Analytics.TestTarget.clientCode = clientcode;

                var server = clientcode + '.tt.omtrdc.net';
                if (typeof mboxVersion == 'undefined') {
                    mboxVersion = 46;
                    mboxFactories = new mboxMap();
                    mboxFactoryDefault = new mboxFactory(server, clientcode, 'default');
                }

                // this diverges from the standard T&T mbox, but client-side simulation breaks if we don't do this
                // the reason is that T&T will set and 'mboxSession' cookie when mboxXDomain == true, which we
                // can't override

                if (this.isInSimulationMode()) {
                    mboxFactories.each(function(mboxFactoryName) {
                        var mboxFactory = mboxFactories.get(mboxFactoryName);
                        mboxFactory.getUrlBuilder().addParameter("mboxXDomain", "disabled");
                    });
                }


                if (mboxGetPageParameter("mboxDebug") != null
                    || mboxFactoryDefault.getCookieManager().getCookie("debug") != null) {
                    setTimeout(
                        function() {
                            if (typeof mboxDebugLoaded == 'undefined') {
                                alert('Could not load the remote debug.\nPlease check your connection to Adobe Target servers');
                            }
                        }, 60 * 60);
                    document.write('<'
                        + 'scr'
                        + 'ipt language="Javascript1.2" src='
                        + '"http://admin4.testandtarget.omniture.com/admin/mbox/mbox_debug.jsp?mboxServerHost='
                        + server + '&clientCode=' + clientcode + '"><'
                        + '\/scr' + 'ipt>');
                }


                mboxVizTargetUrl = function(_) {
                    if (!mboxFactoryDefault.isEnabled()) {
                        return;
                    }

                    var v = mboxFactoryDefault.getUrlBuilder().clone();
                    v.setBasePath('/m2/' + clientcode + '/viztarget');

                    v.addParameter('mbox', _);
                    v.addParameter('mboxId', 0);
                    v.addParameter('mboxCount',
                        mboxFactoryDefault.getMboxes().length() + 1);

                    var pb = new Date();
                    v.addParameter('mboxTime', pb.getTime() -
                        (pb.getTimezoneOffset() * 60000));

                    v.addParameter('mboxPage', mboxGenerateId());

                    var c = mboxShiftArray(arguments);
                    if (c && c.length > 0) {
                        v.addParameters(c);
                    }

                    v.addParameter('mboxDOMLoaded', mboxFactoryDefault.isDomLoaded());

                    mboxFactoryDefault.setVisitorIdParameters(v);

                    return v.buildUrl();
                };
            }

            /**
             * Skip setting the offer for updates arriving too late
             */
            mbox.prototype._setOffer = mbox.prototype.setOffer;
            mbox.prototype.setOffer = function(Nb) {

                // TNT-17129
                if ('_onLoad' in Nb) {
                    Nb.show = function(_mbox) {
                        var _defaultDiv = _mbox.getDefaultDiv();
                        if (_defaultDiv == null) { // this needs to be '==' ,  see CQ5-28207
                            return 0;
                        }

                        var _mboxName = _mbox.getId() ? _mbox.getId() : _mbox.getName();

                        // modification - instead of overriding the onclick element
                        // to look for the the clickDiv which _should_ be present in the
                        // DOM right now instead of looking for it when the div is clicked
                        var _clickDiv = document.getElementById('mboxClick-' + _mboxName);
                        if (_clickDiv && _clickDiv.onclick) {
                            _defaultDiv.onclick = _clickDiv.onclick;
                        }

                        var _result = _mbox.hide();
                        if (_result == 1) {
                            this._onLoad();
                        }
                        return _result;
                    };
                }

                var _mboxName = this.g;
                CQ_Analytics.TestTarget.clearLateMboxArrivalTimeout(_mboxName);

                if (CQ_Analytics.TestTarget.ignoredUpdates[_mboxName]) {
                    delete CQ_Analytics.TestTarget.ignoredUpdates[_mboxName];
                    return this;
                } else {
                    return this._setOffer(Nb);
                }
            };

            /**
             * Removes the default div from this mbox
             *
             * <p>This function is useful when the DOM on the page changes to prevent the cached DOM element from being
             * a disconnected from the actual document.</p>
             */
                // TNT-16921
            mbox.prototype.clearDefaultDiv = function() {
                this.Sb = null;
            };

            mbox.prototype.setUrlBuilder = function(urlBuilder) {
                this.v = urlBuilder;
                this.v.addParameter('mbox', this.g)
                    .addParameter('mboxId', this.Hb);
            };

            mboxCookieManager.prototype.deleteAllCookies = function() {

                var that = this;

                this.gc.each(function(cookieName, cookie) {
                    that.deleteCookie(cookieName);
                });
            };

            mboxFactory.prototype.regenerateSession = function() {

                this.I.deleteAllCookies();
                this.K = mboxGenerateId();


                this.R = new mboxSession(this.K,
                    'mboxSession',
                    'session', 31 * 60, this.I);
                this.S = new mboxPC('PC',
                    1209600, this.I);

                // TODO need to remove reference to CQ_Analytics.TestTarget to submit this upstream; should be a property of the mboxFactory
                this.v = new mboxUrlBuilder(this.B, CQ_Analytics.TestTarget.clientCode);
                this.T(this.v, this.H);
                // see comment in 'init' about the need to set this
                this.v.addParameter('mboxXDomain', 'disabled');

                var that = this;
                this.getMboxes().each(function(mbox) {
                    mbox.setUrlBuilder(that.v.clone());
                });
            };

            mboxOfferDefault.prototype._show = mboxOfferDefault.prototype.show;
            mboxOfferDefault.prototype.show = function(content) {
                var cookiez = getCookieObject();
                if (cookiez["wcmmode"] === "edit"
                    && cookiez["cq-authoring-mode"] === "TOUCH") {
                    var defaultDiv = content.getDefaultDiv();

                    if (defaultDiv.parentNode) {
                        var e = defaultDiv.parentNode.getElementsByTagName("cq")[0];
                        // pull the default content from the repo
                        var defaultContent = pullContent(e.getAttribute("data-path") + ".default.html");
                        if (defaultContent !== null) {
                            // put the new content in the DOM
                            var scriptWrapper = document.createElement("div");
                            scriptWrapper.innerHTML = defaultContent.body;
                            defaultDiv.parentNode.replaceChild(scriptWrapper, defaultDiv);
                            // run the scripts that we may have in the new content
                            var scripts = defaultDiv.getElementsByTagName("script");
                            for (var i = 0; i < scripts.length; i++) {
                                eval(scripts[i].text);
                            }
                        }
                    }
                } else {
                    this._show(content);
                }

            }

        },
        /**
         * Fetches the resource from provided path and writes the
         * response to the document or the mbox Element if
         *
         * <ul>
         * <li>response status code is 200</li>
         * <li>response has a body with length > 0</li>
         * </ul>
         *
         * Uses a synchronous call for requesting the resource. If a WCM mode is defined this
         * call forces the resource to be rendered with WCM mode disabled.
         *
         * @static
         * @param {String}
         *            path Path to document/node to request.
         */
        pull: function(path) {

            // if possible, force WCM mode as disabled to prevent edit decorations from being applied
            var wcmmode = CQ.shared.HTTP.getParameter(document.location.href, 'wcmmode');
            if (typeof CQ.WCM !== 'undefined') {
                wcmmode = "disabled";
            }
            if (wcmmode && wcmmode.length > 0) {
                path = CQ.shared.HTTP.addParameter(path, 'wcmmode', wcmmode);
            }

            // pull in the content
            var output = CQ.shared.HTTP.get(path);
            var isOk = (output && output.status && output.status == 200);
            var hasBody = (output && output.body && output.body.length > 0);

            if (isOk && hasBody) {
                var caller = arguments.callee.caller;
                var outputWritten = false;

                // the target is the div used for the default content
                var target;

                // try and the detect the caller parameters by inspecting the caller stack 
                while (caller) {
                    if (caller.arguments.length > 0) {

                        var mboxParameters = extractMboxParameters(caller.arguments[0]);
                        if (!mboxParameters) {
                            continue;
                        }

                        var mboxName, mboxId, i, entry;

                        for (i = 0; i < mboxParameters.length; i++) {
                            entry = mboxParameters[i];
                            if ('name' in entry && 'value' in entry) {
                                if (entry['name'] === 'mbox') {
                                    mboxName = entry['value'];
                                } else if (entry['name'] === 'mboxId') {
                                    mboxId = entry['value'];
                                }
                            }
                        }


                        target = document.getElementById("mboxImported-default-" + mboxName + "-" + mboxId);
                        break;
                    }
                    caller = caller.arguments.callee.caller;
                }
                ;

                // if a target is found, process
                if (target) {
                    // look for the wrapper div which tracks clicks
                    var childDivs = target.getElementsByTagName('div');
                    if (childDivs.length == 1) {
                        target = childDivs[0];
                    }

                    var scriptwrapper = document.createElement('div');
                    scriptwrapper.innerHTML = output.body;
                    target.appendChild(scriptwrapper);
                    var scripts = target.getElementsByTagName('script');
                    for (var i = 0; i < scripts.length; i++) {
                        eval(scripts[i].text);
                    }

                    outputWritten = true;
                }

                // CQ-15679 - fallback in case we don't find a target div
                if (!outputWritten) {
                    document.write(output.body);
                }
            } else {
                if (console) console.log("Could not pull resource. Response[status:{},body:{}]", output.status, output.body);
            }
        },

        /**
         * Triggers an update of all the registered mboxes
         *
         * <p>Delays the update requests based on the <tt>delay</tt> parameter so that multiple update requests
         * are clumped together.</p>
         *
         * @param delay {Integer} the delay in milliseconds to apply to the reload, defaults to 500
         */
        triggerUpdate: function(delay) {

            if (typeof delay == "undefined")
                delay = 500;

            if (!CQ_Analytics.TestTarget.reloadRequested) {
                CQ_Analytics.TestTarget.reloadRequested = true;
                setTimeout(function() {
                        CQ_Analytics.TestTarget.deleteMboxCookies();
                        CQ_Analytics.TestTarget.reloadRequested = false;
                    },
                    delay);
            }
        },

        registerMboxUpdateCalls: function() {
            if (CQ_Analytics.mboxes) {
                CQ_TestTarget = {};
                CQ_TestTarget.usedStoresLoaded = false;
                CQ_TestTarget.usedStores = CQ_Analytics.TestTarget.getMappedSessionstores();

                var trackStoreUpdate = function(sessionstore) {
                    var idx = $CQ.inArray(sessionstore.getName(), CQ_TestTarget.usedStores);
                    if (idx > -1 && !$CQ.isEmptyObject(sessionstore.getData())) {
                        CQ_TestTarget.usedStores.splice(idx, 1);
                    }
                    if (CQ_TestTarget.usedStores.length < 1 && !CQ_TestTarget.usedStoresLoaded) {
                        var campaignStore = ClientContext.get("campaign");
                        if (campaignStore && campaignStore.isCampaignSelected()) {
                            return;
                        }
                        CQ_Analytics.TestTarget.callMboxUpdate();
                        CQ_TestTarget.usedStoresLoaded = true;
                    }
                };

                if (CQ_TestTarget.usedStores.length > 0) {

                    // iterate over a copy of the stores since trackStoreUpdate potentially
                    // modifies the CQ_TestTarget.usedStores array
                    var usedStoresCopy = CQ_TestTarget.usedStores.slice(0);

                    // 1. handle stores which are already initialized
                    for (var i = 0; i < usedStoresCopy.length; i++) {
                        var storeName = usedStoresCopy[i];
                        var sessionstore = CQ_Analytics.ClientContextMgr.getRegisteredStore(storeName);
                        if (sessionstore.isInitialized()) {
                            trackStoreUpdate(sessionstore);
                        }
                    }

                    // 2. handle stores which are not initialized but trigger events 
                    CQ_Analytics.CCM.addListener("storeupdate", function(e, sessionstore) {
                        trackStoreUpdate(sessionstore);
                    });
                    // fallback in case the store does not call storeupdate 
                    CQ_Analytics.CCM.addListener("storesinitialize", function(e, sessionstore) {
                        if (!CQ_TestTarget.usedStoresLoaded) {
                            var campaignStore = ClientContext.get("campaign");
                            if (campaignStore && campaignStore.isCampaignSelected()) {
                                return;
                            }

                            CQ_Analytics.TestTarget.callMboxUpdate();
                        }
                    });
                } else {

                    var campaignStore = ClientContext.get("campaign");
                    if (campaignStore && campaignStore.isCampaignSelected()) {
                        return;
                    }
                    CQ_Analytics.TestTarget.callMboxUpdate();
                }
            }
        },

        maxProfileParams: 200,

         callMboxUpdate: function() {
            if (CQ_Analytics.mboxes) {
                for (var i = 0; i < CQ_Analytics.mboxes.length; i++) {
                    var updateArgs = [CQ_Analytics.mboxes[i].name];
                    var profileParams = 0;
                    if (!CQ_Analytics.mboxes[i].defined) {
                        var callParameters = [CQ_Analytics.mboxes[i].id,CQ_Analytics.mboxes[i].name];
                        mboxDefine.apply(undefined, callParameters.concat(CQ_Analytics.mboxes[i].staticParameters));
                        CQ_Analytics.mboxes[i].defined = true;
                    }
                    for (var j = 0; j < CQ_Analytics.mboxes[i].mappings.length; j++) {
                        var profileprefix = "";
                        var param = CQ_Analytics.mboxes[i].mappings[j].param;
                        var keypath = '/' + CQ_Analytics.mboxes[i].mappings[j].ccKey.replace('.', '/');
                        if (CQ_Analytics.mboxes[i].isProfile.indexOf(param) > -1) {
                            if (CQ_Analytics.TestTarget.maxProfileParams > 0 && ++profileParams > CQ_Analytics.TestTarget.maxProfileParams) {
                                mboxUpdate.apply(this, updateArgs);
                                updateArgs = [CQ_Analytics.mboxes[i].name];
                                profileParams = 0;
                            }
                            /* we should always apply the prefix, to prevent parameter name collisions */
                            /*if (!param.match(/^profile\..*$/)) {*/
                            profileprefix = "profile.";
                            /*}*/
                        }
                        updateArgs.push(profileprefix + param + "=" + CQ_Analytics.Variables.replaceVariables(CQ_Analytics.ClientContext.get(keypath)));
                    }

                    if (CQ_Analytics.mboxes[i].includeResolvedSegments && CQ_Analytics.SegmentMgr) {
                        var resolvedSegments = CQ_Analytics.SegmentMgr.getResolved();
                        if (resolvedSegments.length > 0) {
                            updateArgs.push('profile._cq_.resolvedSegments=|' + CQ_Analytics.SegmentMgr.getResolved().join('|') + '|');
                        }
                    }
                    /* space out the first call, which is probably the global mbox, by 100 ms,
                     * to give T&T time to process the profile and use it in the next update calls
                     */
                    var that = this;
                    (function(args) {
                        setTimeout(function() {
                            mboxUpdate.apply(that, args)
                        }, (i > 0 ? 100 : 0));
                    })(updateArgs);
                }
            }
        },

        /**
         * Returns an Array of session store names used in
         * CQ_Analytics.mboxes mappings. Returns empty Array if none
         * found.
         */
        getMappedSessionstores: function() {
            var storenames = [];
            if (CQ_Analytics.mboxes) {
                for (var i = 0; i < CQ_Analytics.mboxes.length; i++) {
                    for (var j = 0; j < CQ_Analytics.mboxes[i].mappings.length; j++) {
                        var mapping = CQ_Analytics.mboxes[i].mappings[j].ccKey;
                        var tmp = mapping.split(".");
                        var storename = tmp[0];
                        var key = tmp[1];
                        if ($CQ.inArray(storename, storenames) < 0) {
                            storenames.push(storename);
                        }
                    }
                }
            }
            return storenames;
        },

        /**
         * Returns true if the mbox calls are to me made in simulation mode ( WCM preview and edit modes )
         */
        isInSimulationMode: function() {
            // use CQ.WCM when available
            if (CQ && CQ.WCM) {
                return CQ.WCM.isPreviewMode() || CQ.utils.WCM.isEditMode();
            }
            // fallback to reading the cookies directly
            return isEditOrPreview();
        },

        /**
         * Deletes mbox cookies. If mboxFactoryDefault is undefined the function returns.
         * Forces an mbox update if either of CQ.WCM.isPreviewMode() or CQ.utils.WCM.isEditMode() is true.
         */
        deleteMboxCookies: function() {
            if (typeof mboxFactoryDefault == 'undefined') return;

            if (this.isInSimulationMode()) {
                mboxFactoryDefault.regenerateSession();

                var campaignStore = ClientContext.get("campaign");
                if (campaignStore && campaignStore.isCampaignSelected()) {
                    return;
                }

                CQ_Analytics.TestTarget.callMboxUpdate();
            }
        },

        registerListeners: function() {
            var stores = CQ_Analytics.CCM.getStores();
            for (var storename in stores) {
                var store = stores[storename];
                // completely ignore the mouse store since it's never useful for T&T
                if (storename != "mouseposition" && store.addListener) {
                    store.addListener("update", function(event, property) {
                        // avoid the surferstore getting mouse position updates
                        if (typeof property == 'undefined' ||
                            ( property && property.match && property.match("^mouse") != "mouse"))
                            CQ_Analytics.TestTarget.triggerUpdate();
                    });
                }
            }
        },

        ignoredUpdates: {},

        ignoreNextUpdate: function(mboxName) {
            CQ_Analytics.TestTarget.ignoredUpdates[mboxName] = true;
        },

        /**
         * Adds a new mboxDefinition to the CQ_Analytics.mboxes array
         *
         * <p>Removes any mbox definition with the same mbox id prior to adding the passed
         * mboxDefinition.</p>
         *
         * @return {Boolean} true if an mbox was replaced, false otherwise
         */
        addMbox: function(mboxDefinition) {
            var replaced = false;

            if (!CQ_Analytics.mboxes) {
                CQ_Analytics.mboxes = [];
            }
            for (var i = 0; i < CQ_Analytics.mboxes.length; i++) {

                var mbox = CQ_Analytics.mboxes[i];
                //  cleanup existing mbox
                if (mbox.id == mboxDefinition.id) {
                    CQ_Analytics.mboxes.splice(i, 1);
                    replaced = true;
                    break;
                }
            }

            CQ_Analytics.mboxes.push(mboxDefinition);

            return replaced;
        },

        /**
         * Hides the default content for an mbox
         */
        hideDefaultMboxContent: function(mboxId) {
            $CQ('#' + mboxId).find('div').css('visibility', 'hidden');
        },

        /**
         * Shows the default content for an mbox
         */
        showDefaultMboxContent: function(mboxId, mboxName) {
            var defaultContent = $CQ('#' + mboxId);

            // defaultContent no longer present -> mbox has loaded 
            if (!defaultContent.length)
                return;

            //need to define the mbox now, if it's not already defined

            CQ_Analytics.mboxes.map(function(m) {
                if (m.name === mboxName && !m.defined) {
                    var callParameters = [m.id,m.name];
                    mboxDefine.apply(undefined, callParameters.concat(m.staticParameters));
                    m.defined = true;
                }
            });

            mboxFactoryDefault.get(mboxName).show(new mboxOfferDefault());
            CQ_Analytics.TestTarget.ignoreNextUpdate(mboxName);
        },

        ignoreLateMboxArrival: function(mboxId, mboxName, timeout) {

            this.clearLateMboxArrivalTimeout(mboxId);

            var that = this;
            this.lateMboxArrivalTimeouts[mboxId] = setTimeout(function() {
                that.showDefaultMboxContent(mboxId, mboxName);
                that.clearLateMboxArrivalTimeout(mboxId);
            }, timeout);
        },

        clearLateMboxArrivalTimeout: function(mboxId) {
            if (this.lateMboxArrivalTimeouts[mboxId]) {
                clearTimeout(this.lateMboxArrivalTimeouts[mboxId]);
                delete this.lateMboxArrivalTimeouts[mboxId];
            }
        }
    }
};

// restore previous attributes
if (typeof oldTandT !== 'undefined ') {
    for (var prop in oldTandT) {
        CQ_Analytics.TestTarget[prop] = oldTandT[prop];
    }
}