var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/// <reference path="../base/BaseClass.ts" />
/// <reference path="../env/LocalStorage.ts" />
/// <reference path="../env/SessionStorage.ts" />
/// <reference path="../privacyprotection/ProfilePermission.ts" />
/// <reference path="../util/Json.ts" />
/// <reference path="../data.ts" />
/// <reference path="../profileaccess/PublicEndpointRequest.ts" />
var econda;
(function (econda) {
    var privacyprotection;
    (function (privacyprotection) {
        var LocalStorage = econda.env.LocalStorage;
        var ProfilePermission = econda.privacyprotection.ProfilePermission;
        var SessionStorage = econda.env.SessionStorage;
        var Json = econda.util.Json;
        /**
         * Class for helper function for privacy protection.
         *
         * @class econda.privacyprotection.EmosPrivacy
         */
        var EmosPrivacy = (function (_super) {
            __extends(EmosPrivacy, _super);
            function EmosPrivacy() {
                _super.apply(this, arguments);
            }
            EmosPrivacy.PRIVACY_PROTECTION_KEY = 'econda.privacy.protection';
            EmosPrivacy.PROFILE_STATE_KEY = 'profile.state';
            EmosPrivacy.PRIVACY_PROTECTION_UPDATED_HASH = 'econda.privacy.updatehash';
            return EmosPrivacy;
        }(econda.base.BaseClass));
        privacyprotection.EmosPrivacy = EmosPrivacy;
        /**
         * Lockup for localstorage permissions.profile.state
         *
         * @returns {boolean} if ALLOW
         */
        privacyprotection.hasProfileOptIn = function () {
            var permissions = privacyprotection.getPermissionsFromLocalStorage();
            return permissions.profile.state === "ALLOW";
        };
        /**
         * Lockup for localstorage permissions.profile.version
         *
         * @returns {string} the version
         */
        privacyprotection.getProfileOptInVersion = function () {
            var permissions = privacyprotection.getPermissionsFromLocalStorage();
            return permissions.profile.version;
        };
        /**
         * Lockup for localstorage channel (e.g. BILLING) permission state
         *
         * @returns {boolean} if ALLOW
         */
        privacyprotection.hasChannelOptIn = function (channel) {
            var permissions = privacyprotection.getPermissionsFromLocalStorage();
            var state = permissions.channels[channel].state;
            return state === "ALLOW";
        };
        /**
         * Wrapper function for personal data that returns the original value in case of opt-in or an empty value otherwise.
         *
         * Personal data must only be transferred and stored if the current visitor has given the according permission.
         * This function reads and applies the current privacy settings. Personal data includes identifiers such as user ID or email hash.
         *
         * This function should be used to wrap all personal data as input to the econda Analytics library (emos.js).
         * Based on the default events measured in Analytics, this includes login, registration and order.
         *
         * <h3> Example for login:</h3>
         *
         * <script type="text/javascript">
         *      window.emos3.send({
         *          content: 'login/success',    // page name
         *           login: [econda.privacyprotection.emptyIfNotProfileOptIn('4A923bDc'), 0] // login
         *      });
         *  </script>
         * </pre>
         *
         * This wrapper function can only be used for optional values. For values that need to be non empty and unique
         * (such as the order number)  {@link econda.privacyprotection.EmosPrivacy econda.privacyprotection.anonymiseIfNotProfileOptIn(value)} must be used
         *
         * @param {string} value the value to check
         * @returns {string} value or empty
         */
        privacyprotection.emptyIfNotProfileOptIn = function (value) {
            var permission = privacyprotection.getPermissionsFromLocalStorage();
            if (permission.profile.state === "ALLOW") {
                return value;
            }
            else {
                return '';
            }
        };
        /**
         * Wrapper function for personal data that returns the original value in case of opt-in or an unique anonymous
         * value otherwise.
         *
         * Personal data must only be transferred and stored if the current visitor has given the according permission.
         * This function reads and applies the current privacy settings. Personal data includes identifiers such as the order number.
         *
         * This function should be used to wrap all personal data as input to the econda Analytics library (emos.js)
         * that needs to be non empty and unique. In the default data model, the order number as part of the order event is the only effected value.:
         *
         * <h3>Example for order:</h3>
         *
         * <script type="text/javascript">
         *      window.emos3.send({
         *           content: 'checkout/confirmation',    // page name
         *           billing:  [                          // billing informat
         *           econda.privacyprotection.anonymiseIfNotProfileOptIn('I-501-1234-23'),
         *           econda.privacyprotection.emptyIfNotProfileOptIn('4A923bDc'),
         *           'DE/Karlsruhe/76135',
         *           29.90
         *           ],
         *           ec_Event:  [   // purchased products
         *              {
         *              type: 'buy',
         *              pid: 'P-1003',
         *              sku: 'P-1003-001-23-125',
         *              name: 'POWERFIX overall',
         *              price: 29.90,
         *              group: 'men/workwear',
         *              count: 1,
         *              var1: 'XXL',
         *              var2: 'blue',
         *              var3: 'multipockets'
         *              }
         *          ]
         *      });
         *  </script>
         *
         * Most of personal data is optional in econda Analytics. For optional data the wrapper function
         * {@link econda.privacyprotection.EmosPrivacy econda.privacyprotection.emptyIfNotProfileOptIn()}
         * should be used.
         *
         * @param {string} value the value to anonymized
         * @returns {string} the anonymized value
         */
        privacyprotection.anonymiseIfNotProfileOptIn = function (value) {
            var permission = privacyprotection.getPermissionsFromLocalStorage();
            if (permission.profile.state === "ALLOW") {
                return value;
            }
            else {
                var anonymisedValue = SessionStorage.getItem("ecAnonValues." + value);
                if (!anonymisedValue || anonymisedValue === '') {
                    anonymisedValue = "X-" + Date.now().toString(16) + Math.floor(Math.random() * 4294967296).toString(16);
                    SessionStorage.setItem("ecAnonValues." + value, anonymisedValue);
                }
                return anonymisedValue;
            }
        };
        /**
         * Updates the privacy settings for the current visitor from the backend. The main scenario is the propagation of
         * opt-outs or updates of channel permissions.
         *
         * These permissions could have been updated on other devices or using other channels (e.g. call center).
         * updating the settings from the backend applies the changes to the current device and therefore synchronizes
         * the privacy settings across devices and channels.
         *
         * As prerequisite, a profile endpoint with key 'ENDPOINT-KEY' has to be created in the econda ARP UI, that
         * returns the profile properties 'permissions:profile' and 'permissions:channels' without authentication
         * <a href="http://doku.econda.org/display/CSDE/Endpoints+erstellen">LINK: Endpoints+erstellen</a>  .
         *
         * As result, the current privacy settings will be written or updated in local storage. The update is
         * asynchronous and will have effect on future access, e.g. the next click or page load.
         *
         * The actual call to the service will only be triggered if new ID information is available.
         * This includes changes in the Cookie ID as well as new logins
         *
         * {@link econda.recengine.VisitorProfile#login econda.data.visitor.login}
         *
         * In case of no changes there will be no operation.
         *
         * @param {string} clientKey access for client
         * @param {string} endpointKey key for specific endpoint
         */
        privacyprotection.updatePrivacySettingsFromBackend = function (clientKey, endpointKey) {
            if (!clientKey || typeof clientKey !== "string" || !endpointKey || typeof endpointKey !== "string") {
                econda.debug.log('updatePrivacySettingsFromBackend called with incorrect input', clientKey, endpointKey);
                return;
            }
            if (!econda.data.visitor.getVisitorId()) {
                return;
            }
            var actualHash = SessionStorage.getItem(EmosPrivacy.PRIVACY_PROTECTION_UPDATED_HASH);
            var currentIds = econda.data.visitor.getIds();
            if (actualHash === privacyprotection._getHashCodeFor(currentIds)) {
                return;
            }
            privacyprotection._callProfileEndpoint(clientKey, endpointKey);
            SessionStorage.setItem(EmosPrivacy.PRIVACY_PROTECTION_UPDATED_HASH, privacyprotection._getHashCodeFor(currentIds));
        };
        /**
         * Get current privacy settings from local storage and return ProfilePermission. ProfilePermission has a profile
         * with state "UNKNOWN||DENY||ALLOW" and Channels with the same structure.
         *
         * Main scenario is e.g. to check for each contact the profile permission state and inform the visitor explicit
         * about options to contradict collection of personal data, in case that permission state is "UNKNOWN".
         *
         * If local storage is not available or no OptIn/OptOut is set permissions.profile.state is UNKNOWN.
         *
         * For more information follow the link:
         *  <a href="https://support.econda.de/display/CSDE/Anforderungen+aus+DSGVO">LINK: Anforderungen+aus+DSGVO</a>
         *
         * @returns {econda.privacyprotection.ProfilePermission}
         */
        privacyprotection.getPermissionsFromLocalStorage = function () {
            var permissions = new ProfilePermission();
            if (LocalStorage.isAvailable()) {
                var privacyProtectionData = LocalStorage.getItem(EmosPrivacy.PRIVACY_PROTECTION_KEY);
                if (privacyProtectionData) {
                    try {
                        //privacySettings = JSON.parse(privacyProtectionData);
                        permissions = JSON.parse(privacyProtectionData);
                    }
                    catch (error) {
                        console.log("Error parsing item from local storage");
                    }
                }
                else {
                    permissions.profile.state = econda.privacyprotection.STATE_UNKNOWN;
                }
            }
            else {
                permissions.profile.state = econda.privacyprotection.STATE_UNKNOWN;
            }
            return permissions;
        };
        /**
         * Function to change the current privacy settings. The settings will be updated on the current device, and
         * additionally the updated will be added to an emos property object. The privacy settings in the backend are updated by sending this modified emos property object.
         *
         * The format of the updated corresponds to the format of permissions that are returned from the backend by
         * profile endpoints.
         *
         * <h3>Example:</h3>
         *
         *   <script type="text/javascript">
         *      window.emosProps = {...};
         *
         *       econda.privacyprotection.applyAndStoreNewPrivacySettings(
         *       window.emosProps,
         *           {
         *            "permissions:profile": {
         *                state: "ALLOW|DENY",
         *                version: "VERSION",
         *                source: "SOURCE"
         *            },
         *            "permissions:channels": {
         *                "CHANNEL": {
         *                    state: "ALLOW|DENY",
         *                    version: "VERSION",
         *                    source: "SOURCE"
         *                }
         *            }
         *           }
         *       );
         *       window.emos3.send(window.emosProps);
         *
         *  </script>
         *
         * Updates of the profile permissions as well as updates of channel permissions are optional, only updated values
         * should be given as input to this function.
         *
         * The update of the backend is asynchronous and will be active in the timespan of ~5 minutes. The privacy
         * settings on the current device are stored locally and are active immediately.
         *
         * In case of profile opt-out ("permissions:profile"."state" = DENY), the current cookie ID will still be used to
         * send the privacy settings updates. The cookie ID will be deleted before the sending the next page impression when calling
         * {@link econda.privacyprotection.EmosPrivacy econda.privacyprotection.setEmos3PrivacySettings()}
         *
         * @param {Object} emosProps actual props
         * @param {Object} externalPermissions the new permissions to merge
         */
        privacyprotection.applyAndStoreNewPrivacySettings = function (emosProps, externalPermissions) {
            var newPermissions = privacyprotection._convertExternalPermissionsToProfilePermissions(externalPermissions);
            var currentPermissions = privacyprotection.getPermissionsFromLocalStorage();
            var mergedPermissions = privacyprotection._mergePermissions(currentPermissions, newPermissions);
            LocalStorage.setItem(EmosPrivacy.PRIVACY_PROTECTION_KEY, JSON.stringify(mergedPermissions));
            var isOptOutAction = newPermissions.profile.state == "DENY" && currentPermissions.profile.state == "ALLOW";
            // emos Privacy Einstellungen bei Opt-Out basierend auf altem Stand,
            // ansonsten basierend auf gemergetem Stand setzen
            // -> bei Opt-Out wird somit für den aktuellen PI noch das ALLOW verwendet
            _setEmos3PrivacySettingsBasedOn(isOptOutAction ? currentPermissions : mergedPermissions);
            _setEmosArpProps(mergedPermissions, emosProps);
        };
        /**
         * Note: Its intended to allow duplicated permissions in ARPPROPS for permissions. The user should call this
         * function only once at the beginning of site rendering. Duplications will be ignored at the backend.
         *
         * @param {econda.privacyprotection.ProfilePermission} mergedPermissions
         * @param {Object} emosProps
         * @private
         */
        function _setEmosArpProps(mergedPermissions, emosProps) {
            if (!mergedPermissions) {
                return;
            }
            if (!emosProps["arpprops"]) {
                emosProps["arpprops"] = [];
            }
            if (mergedPermissions.profile) {
                var state = mergedPermissions.profile.state ? mergedPermissions.profile.state : '';
                var version = mergedPermissions.profile.version ? mergedPermissions.profile.version : '';
                var source = mergedPermissions.profile.source ? mergedPermissions.profile.source : '';
                var profilePermission = "profile/" + state + "/" + version + "/" + source;
                emosProps["arpprops"].push(["PERMISSION", profilePermission]);
            }
            var channelKeys = Object.keys(mergedPermissions.channels);
            for (var i = 0; i < channelKeys.length; i++) {
                var channel = channelKeys[i];
                var state = mergedPermissions.channels[channel].state ? mergedPermissions.channels[channel].state : '';
                var version = mergedPermissions.channels[channel].version ? mergedPermissions.channels[channel].version : '';
                var source = mergedPermissions.channels[channel].source ? mergedPermissions.channels[channel].source : '';
                var channelPermission = "channel/" + channel + "/" + state + "/" + version + "/" + source;
                emosProps["arpprops"].push(["PERMISSION", channelPermission]);
            }
        }
        privacyprotection._setEmosArpProps = _setEmosArpProps;
        /**
         * Reads the current privacy settings and applies all relevant settings to the econda Analytics library (emos.js).
         *
         * If there are no changes to permission, this function should be called before triggering the measurement:
         *
         * <h3>Example:</h3>
         *
         * <script type="text/javascript">
         *     econda.privacyprotection.setEmos3PrivacySettings();
         *     window.emos3.send(...);
         *  </script>
         * </pre>
         *
         * In case of opt-in, the cookie ID is used or generated. In case of opt-out, the cookie ID will not be used and
         * won't be generated if not present.
         *
         * If there are changes to the privacy settings, the function
         * {@link econda.privacyprotection.EmosPrivacy econda.privacyprotection.applyAndStoreNewPrivacySettings()}
         * has to be used instead of this function.
         */
        privacyprotection.setEmos3PrivacySettings = function () {
            var permissions = privacyprotection.getPermissionsFromLocalStorage();
            _setEmos3PrivacySettingsBasedOn(permissions);
        };
        var _setEmos3PrivacySettingsBasedOn = function (permissions) {
            if (window['emos3']) {
                if (permissions.profile.state === "ALLOW") {
                    window['emos3'].VCL = 730;
                    // Aktiviere Auslesen der Recipient-ID
                    window['emos3'].PARAM_TO_PROP_MERGE = {
                        ecmUid: 'newsuid'
                    };
                }
                else {
                    // Visitor-Cookie löschen falls vorhanden
                    var hostname = window.location.hostname;
                    document.cookie = privacyprotection._getVisitorCookieToOverwrite(hostname);
                    window['emos3'].VCL = 0;
                    // Deaktiviere Auslesen der Recipient-ID
                    window['emos3'].PARAM_TO_PROP_MERGE = {
                        ecmUid: null
                    };
                }
            }
        };
        privacyprotection._getVisitorCookieToOverwrite = function (hostname) {
            var domainParts = hostname.split('.');
            var cookieDomain = hostname;
            if (domainParts.length > 1) {
                var lastPart = domainParts[domainParts.length - 1];
                var secondLastPart = domainParts[domainParts.length - 2];
                cookieDomain = '.'.concat(secondLastPart, '.', lastPart);
            }
            var cookieName = "emos_jcvid=";
            return cookieName + "; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=" + cookieDomain + ";";
        };
        privacyprotection._callProfileEndpoint = function (clientKey, endpointKey) {
            var request = new econda.profileaccess.PublicEndpointRequest({
                accountId: clientKey,
                endpointKey: endpointKey,
                context: {
                    appendVisitorData: true
                },
                success: function (response) {
                    privacyprotection._endpointCallback(response);
                },
                error: function (error) {
                    econda.debug.log('Error calling profile endpoint to get permissions' + endpointKey, error);
                }
            });
            request.send();
        };
        privacyprotection._endpointCallback = function (response) {
            var serverPermissions = privacyprotection._convertExternalPermissionsToProfilePermissions(response);
            var browserPermissions = privacyprotection.getPermissionsFromLocalStorage();
            var mergedPermissions = privacyprotection._mergePermissions(browserPermissions, serverPermissions);
            LocalStorage.setItem(EmosPrivacy.PRIVACY_PROTECTION_KEY, JSON.stringify(mergedPermissions));
        };
        privacyprotection._convertExternalPermissionsToProfilePermissions = function (externalSettings) {
            var permissionsToReturn = new ProfilePermission();
            var responseProfile = externalSettings["permissions:profile"];
            var parsePassedDateStringOrNow = function (responseTimestamp) {
                // timestamps returned from server are always ISO formatted strings,
                // see e.g. here http://confluence.econda.org/display/IN/Rohprofile+aus+API+laden+und+in+S3+oder+HDFS+ablegen
                return responseTimestamp
                    ? Date.parse(responseTimestamp)
                    : new Date().getTime();
            };
            if (responseProfile) {
                var timestamp = parsePassedDateStringOrNow(responseProfile.timestamp);
                permissionsToReturn.profile = {
                    state: responseProfile.state,
                    version: responseProfile.version,
                    source: responseProfile.source,
                    timestamp: timestamp
                };
            }
            var responseChannels = externalSettings["permissions:channels"];
            if (responseChannels) {
                permissionsToReturn.channels = {};
                var allProperties = Object.getOwnPropertyNames(responseChannels);
                for (var i = 0; i < allProperties.length; i++) {
                    var channel = allProperties[i];
                    var timestamp = parsePassedDateStringOrNow(responseChannels[channel].timestamp);
                    permissionsToReturn.channels[channel] = {
                        state: responseChannels[channel].state,
                        version: responseChannels[channel].version,
                        source: responseChannels[channel].source,
                        timestamp: timestamp
                    };
                }
            }
            return permissionsToReturn;
        };
        privacyprotection._mergePermissions = function (permissionsA, permissionsB) {
            var mergedSettings = new ProfilePermission();
            if (!permissionsB.profile.timestamp) {
                mergedSettings.profile = permissionsA.profile;
            }
            else if (!permissionsA.profile.timestamp) {
                mergedSettings.profile = permissionsB.profile;
            }
            else {
                mergedSettings.profile = permissionsA.profile.timestamp > permissionsB.profile.timestamp
                    ? permissionsA.profile
                    : permissionsB.profile;
            }
            mergedSettings.channels = {};
            var permissionsAKeys = Object.keys(permissionsA.channels);
            var permissionsBKeys = Object.keys(permissionsB.channels);
            var allKeys = permissionsAKeys.concat(permissionsBKeys);
            for (var i = 0; i < allKeys.length; i++) {
                var key = allKeys[i];
                if (!permissionsB.channels[key]) {
                    mergedSettings.channels[key] = permissionsA.channels[key];
                }
                else if (!permissionsA.channels[key]) {
                    mergedSettings.channels[key] = permissionsB.channels[key];
                }
                else {
                    mergedSettings.channels[key] = permissionsA.channels[key].timestamp > permissionsB.channels[key].timestamp
                        ? permissionsA.channels[key]
                        : permissionsB.channels[key];
                }
            }
            return mergedSettings;
        };
        /**
         * Internal hash function. Just an easy  shifting thing.
         *
         * @param {Object} obj  the object which should be hashed
         * @returns {string} the hash
         * @private
         */
        privacyprotection._getHashCodeFor = function (obj) {
            if (!obj) {
                return;
            }
            var objAsString = Json.stringify(obj);
            var hash = 0;
            var length = objAsString.length;
            var i = 0;
            if (length > 0) {
                while (i < length) {
                    hash = (hash << 5) - hash + objAsString.charCodeAt(i++) | 0;
                }
            }
            return hash.toString();
        };
    })(privacyprotection = econda.privacyprotection || (econda.privacyprotection = {}));
})(econda || (econda = {}));