var ECategorySwitchStates = Object.freeze({
    "ENABLE_ALL": "enabled",
    "DISABLE_ALL": "disabled",
    "PARTIAL": "partial"
});

var ECategoryNames = Object.freeze({
    "CATEGORY_MANDATORY": "tagcategory-notwendig",
    "CATEGORY_FUNCTIONAL": "tagcategory-funktional",
    "CATEGORY_MARKETING": "tagcategory-marketing"
});

/**
 * @constructor
 * @param {string} consentId - The consent element id.
 * @param {string} consentOptionsId - The consent settings element id
 * @param {string} consentTemplateCategory - A template with placeholders to create the category html from
 * @param {string} consentTemplateCategoryContent - A template with placeholders to create the category content html from
 * @param {string} consentTemplateSingleTag - A template with placeholders to create the single tag html from
 * @param {string} isoCode - The country iso code
 * @param {string} uid - The UID of the shop
 * @param {string} prefix - The local storage prefix
 * @param {string} exceptionRegEx - Regex where consent layer don't need to be displayed
 * @param {string} homeUrl - Url of welcome page
 */
function ConsentLayer(
    consentId,
    consentOptionsId,
    consentTemplateCategory,
    consentTemplateCategoryContent,
    consentTemplateSingleTag,
    isoCode,
    uid,
    prefix,
    exceptionRegEx,
    homeUrl
) {
    "use strict";
    this.consentId = consentId;
    this.consentOptionsId = consentOptionsId;
    this.consentTemplateCategory = consentTemplateCategory;
    this.consentTemplateCategoryContent = consentTemplateCategoryContent;
    this.consentTemplateSingleTag = consentTemplateSingleTag;
    this.hElement = undefined;
    this.hElement2 = undefined;
    this.isInitialized = false;
    this.isoCode = isoCode;
    this.uid = uid;
    this.prefix = prefix;
    try {
        this.exceptionRegEx = new RegExp(exceptionRegEx);
    } catch(e) {
        this.exceptionRegEx = new RegExp("do/not/match");
    }
    this.homeUrl = homeUrl;
    this.showConsentLayer = false;
    this.options = [];
    this.storedConfigurationTooOld = false;
    this.localStorageName = this.prefix + "_consent";
    this.editMode = false;
    this.callback = function() {}
}

/**
 * Open Consent Layer
 */
ConsentLayer.prototype.openConsentLayer = function() {
    "use strict";
    document.getElementById(this.consentId).style.display = null;
};

/**
 * Close Consent Layer
 */
ConsentLayer.prototype.closeConsentLayer = function() {
    "use strict";
    document.getElementById(this.consentId).style.display = "none";
};

/**
 * Generate time of consent
 */
ConsentLayer.prototype.setConsentTime = function() {
    "use strict";
    var userTimeStamp = new Date();
    var dd = String(userTimeStamp.getDate()).padStart(2, "0");
    var mm = String(userTimeStamp.getMonth() + 1).padStart(2, "0");
    var yyyy = userTimeStamp.getFullYear();
    var hh = String(userTimeStamp.getHours()).padStart(2, "0");
    userTimeStamp = yyyy+mm+dd+hh;

    this.options.consentTime = userTimeStamp;
};

/**
 * Handle click on settings
 */
ConsentLayer.prototype.openOptionsDialog = function() {
    "use strict";

    this.closeConsentLayer();

    document.getElementById(this.consentOptionsId).style.display = null;
};

/**
 * Close the options dialog
 */
ConsentLayer.prototype.closeOptionsDialog = function() {
    "use strict";

    document.getElementById(this.consentOptionsId).style.display = "none";
};

/**
 *
 * @param {string} categoryId - Category ID
 * @param {string} cookieName - Name of cookie
 * @param {boolean} state - state to set (true -> on, false -> off)
 */
ConsentLayer.prototype.setOption = function (categoryId, cookieName, state) {
    "use strict";

    for (var i = 0; i < this.options.categories.length; i++) {
        if (this.options.categories[i].categoryId === categoryId) {
            for (var j = 0; j < this.options.categories[i].tagList.length; j++) {
                if (this.options.categories[i].tagList[j].cookieName === cookieName) {
                    this.options.categories[i].tagList[j].active = state;

                    return;
                }
            }
        }
    }
};

/**
 * Lookup if Option is active from configuration
 * @param {string} cookieName - Name of configuration
 *
 * @return boolean
 */
ConsentLayer.prototype.isOptionActive = function(cookieName) {
    "use strict";

    for (var i = 0; i < this.options.categories.length; i++) {
        for (var j = 0; j < this.options.categories[i].tagList.length; j++) {
            if (this.options.categories[i].tagList[j].cookieName === cookieName) {
                return this.options.categories[i].tagList[j].active;
            }
        }
    }

    return false;
};

/**
 * Active option with cookie Name
 * @param {string} cookieName
 */
ConsentLayer.prototype.activateOption = function(cookieName) {
    "use strict";

    for (var i = 0; i < this.options.categories.length; i++) {
        for (var j = 0; j < this.options.categories[i].tagList.length; j++) {
            if (this.options.categories[i].tagList[j].cookieName === cookieName) {
                this.options.categories[i].tagList[j].active = true;

                return;
            }
        }
    }
};

/**
 * Check if Google Maps is agreed too
 * @returns {boolean}
 */
ConsentLayer.prototype.isGoogleMapsAllowed = function() {
    "use strict";

    return this.isOptionActive("userconsent_googlemaps");
};

/**
 * Activate Google Maps
 */
ConsentLayer.prototype.activateGoogleMaps = function() {
    "use strict";

    this.activateOption("userconsent_googlemaps");
    this.saveOptions();
};

/**
 * Toggle a class on an element
 * @param {HTMLElement} element
 * @param {string} className
 */
ConsentLayer.prototype.toggleClass = function(element, className) {
    "use strict";
    if (element.classList) {
        element.classList.toggle(className);
    } else {
        var classes = element.className.split(" ");
        var i = classes.indexOf(className);
        if (i >= 0) {
            classes.splice(i, 1);
        }
        else {
            classes.push(className);
        }
        element.className = classes.join(" ");
    }
};

/**
 * Toggle Options Accordeon
 * @param {Event} event
 */
ConsentLayer.prototype.toggleAccordeon = function(event) {
    "use strict";
    this.toggleClass(event.target.parentElement, 'open');
    event.target.parentElement.scrollIntoView({
        block: "start",
        behavior: "smooth"
    });
};

/**
 * Open a category in the options dialog using the names provided by ECategoryNames
 * @param categoryName
 */
ConsentLayer.prototype.openCategory = function(categoryName) {
    "use strict";
    var element = document.getElementById(categoryName);
    this.toggleClass(element, 'open');
    element.scrollIntoView({
        block: "start",
        behavior: "smooth"
    });
};

/**
 * Load google tag manager
 */
ConsentLayer.prototype.loadGoogleTagManager = function() {
    "use strict";
    window.document.head.append(this.hElement);
    window.document.head.append(this.hElement2);
    this.isInitialized = true;
};

/**
 * Push data to the GTM
 * @param {string} eventName
 */
ConsentLayer.prototype.pushDataLayer = function(eventName) {
    "use strict";
    var event = {
        "event": eventName
    };

    window.dataLayer.push(event);
};

/**
 * Accessed when clicked on "resetconstcookieagreement" class
 * @param {Event} event
 */
ConsentLayer.prototype.changeConsentAgreement = function(event) {
    "use strict";
    event.preventDefault();

    this.editMode = true;
    this.openConsentLayer();
};

/**
 * Selecting matching options from options array
 * @param {object} options
 */
ConsentLayer.prototype.selectOptions = function(options) {
    "use strict";

    if (!options.hasOwnProperty(this.uid)) {
        console.error('Configuration for ' + this.uid + ' not found !');
    }

    this.options = options[this.uid];
};

/**
 * Create Category and fill in placeholders
 * @param {object} category - Category object
 */
ConsentLayer.prototype.createOptionsCategory = function (category) {
    "use strict";
    var node = document.getElementById(this.consentTemplateCategory);

    var clone = node.cloneNode(true);
    clone.removeAttribute("id");
    clone.id = category.categoryId;
    clone.style.display = null;
    clone.innerHTML = clone.innerHTML.replace(/{%categoryName%}/, category.categoryName);
    clone.innerHTML = clone.innerHTML.replace(/{%categoryId%}/g, category.categoryId);

    var elements = document.getElementsByClassName("cookie-consent__settings-page");
    elements[elements.length - 1].appendChild(clone);

    this.createOptionsCategoryContent(category);

    var tags = category.tagList;
    var allActive = true;
    var countActive = 0;
    for (var i = 0; i < tags.length; i++) {
        if (!(tags[i].active)) {
            allActive = false;
        }

        if (!!tags[i].active) {
            countActive++;
        }

        this.createOptionsTag(tags[i], category.categoryId);
    }

    if (category.categoryId === ECategoryNames.CATEGORY_MANDATORY) {
        return;
    }

    var suffix = "";
    if (!!allActive) {
        suffix = "--enabled";
    } else if (!allActive && countActive === 0) {
        suffix = "--disabled";
    } else {
        suffix = "--partial";
    }

    document.getElementById(category.categoryId + suffix).style.display = "block";
};

/**
 * Create Category content and fill in placeholders
 * @param {object} category - Category object
 */
ConsentLayer.prototype.createOptionsCategoryContent = function (category) {
    "use strict";
    var node = document.getElementById(this.consentTemplateCategoryContent);

    var clone = node.cloneNode(true);
    clone.removeAttribute("id");
    clone.style.display = null;
    clone.innerHTML = clone.innerHTML.replace(/{%categoryText%}/, category.categoryText);

    var elements = document.getElementsByClassName("cookie-consent__settings-page");
    elements[elements.length - 1].appendChild(clone);
};

/**
 * Create Category Tag and fill in placeholders
 * @param {object} tag - Tag object
 * @param {string} categoryId - Category ID
 */
ConsentLayer.prototype.createOptionsTag = function (tag, categoryId) {
    "use strict";
    var node = document.getElementById(this.consentTemplateSingleTag);

    var clone = node.cloneNode(true);
    clone.removeAttribute("id");
    clone.style.display = null;
    clone.innerHTML = clone.innerHTML.replace(/{%tagName%}/, tag.tagName);
    clone.innerHTML = clone.innerHTML.replace(/{%cookieName%}/, tag.cookieName);
    clone.innerHTML = clone.innerHTML.replace(/{%categoryId%}/gm, categoryId);

    var input = clone.querySelector("input");
    if (!tag.disabled) {
        input.disabled = false;
    }

    if (!tag.active) {
        input.checked = false;
    }

    var elements = document.getElementsByClassName("cookie-consent__tag-list");
    elements[elements.length - 1].appendChild(clone);
};

/**
 * Write to local storage
 * @param {string} key
 * @param {string} value
 */
ConsentLayer.prototype.writeLocalStorage = function (key, value) {
    "use strict";
    localStorage.setItem(key, value);
};

/**
 * Remove item from local storage
 * @param {string} key
 */
ConsentLayer.prototype.clearLocalStorage = function (key) {
    "use strict";
    localStorage.removeItem(key);
};

/**
 * Read from local storage
 * @param {string} key
 * @returns {string|null}
 */
ConsentLayer.prototype.readLocalStorage = function (key) {
    "use strict";
    return localStorage.getItem(key);
};

/**
 * Load Configuration from local storage if exists
 */
ConsentLayer.prototype.loadOptions = function() {
    "use strict";
    var storedConfiguration = this.readLocalStorage(this.localStorageName);
    if (storedConfiguration === null) {
        return;
    }
    storedConfiguration = JSON.parse(storedConfiguration);
    if (Number(storedConfiguration.lastChanged) < Number(this.options.lastChanged)) {
        this.storedConfigurationTooOld = true;

        return;
    }
    this.options = storedConfiguration;
};

/**
 * Clear all consent local storage entries for current mandant
 */
ConsentLayer.prototype.clearCurrentConsent = function () {
    "use strict";
    this.clearLocalStorage(this.localStorageName);

    var storedKeys = [];
    for (var i = 0; i < localStorage.length; i++) {
        if (localStorage.key(i).substring(0, 15) === this.prefix + "_userconsent") {
            storedKeys.push(localStorage.key(i));
        }
    }

    for (i = 0; i < storedKeys.length; i++) {
        this.clearLocalStorage(storedKeys[i]);
    }
};

/**
 * Save configuration in local storage
 */
ConsentLayer.prototype.saveOptions = function() {
    "use strict";
    this.setConsentTime();
    this.writeLocalStorage(this.localStorageName, JSON.stringify(this.options));

    this.applyConsent();
};

/**
 * Initialize the options dialog
 */
ConsentLayer.prototype.initOptions = function() {
    "use strict";

    var categories = this.options.categories;

    for (var i = 0; i < categories.length; i++) {
        this.createOptionsCategory(categories[i]);
    }

    document.getElementById(ECategoryNames.CATEGORY_MANDATORY + "--mandatory").style.display = "block";
};

/**
 * Initialize the Consent Layer
 * @param {object} options
 */
ConsentLayer.prototype.init = function(options) {
    "use strict";
    if (this.isInitialized) {
        return;
    }

    this.selectOptions(options);
    this.loadOptions();
    this.initOptions();

    if (!!this.storedConfigurationTooOld || this.readLocalStorage(this.localStorageName) === null) {
        this.clearCurrentConsent();
    }

    var url = window.location.href;
    var match = url.match(this.exceptionRegEx);
    if (match === null) {
        this.showConsentLayer = true;
    }

    if ((!this.isAccepted() || this.needReapproval()) && this.showConsentLayer) {
        this.openConsentLayer();
    }
};

/**
 * Toggle checkbox functionality
 * @param {HTMLElement} element - HTML Element of checkbox
 * @param {string} categoryId - Category ID
 * @param {boolean} state - State to set (true -> on, false -> off)
 */
ConsentLayer.prototype.setCheckbox = function (element, categoryId, state) {
    "use strict";
    element.checked = state;

    this.setOption(categoryId, element.id, state);
};

/**
 * on click entry point for check box
 * @param {Event} event - Onclick Event
 * @param {string} categoryId - Category ID
 */
ConsentLayer.prototype.toggleCheckbox = function (event, categoryId) {
    "use strict";
    var element = event.currentTarget;
    this.setCheckbox(element, categoryId, element.checked);

    var sliderEnabled = document.getElementById(categoryId + "--" + ECategorySwitchStates.ENABLE_ALL);
    var sliderDisabled = document.getElementById(categoryId + "--" + ECategorySwitchStates.DISABLE_ALL);
    var sliderPartial = document.getElementById(categoryId + "--" + ECategorySwitchStates.PARTIAL);

    var allForCategory = document.getElementsByClassName("cc-" + categoryId);

    var elementsEnabled = 0;
    for (var i = 0; i < allForCategory.length; i++) {
        if (!!allForCategory[i].checked) {
            elementsEnabled++;
        }
    }

    sliderEnabled.style.display = "none";
    sliderDisabled.style.display = "none";
    sliderPartial.style.display = "none";
    if (elementsEnabled === 0) {
        sliderDisabled.style.display = "block";
    } else if (elementsEnabled === allForCategory.length) {
        sliderEnabled.style.display = "block";
    } else {
        sliderPartial.style.display = "block";
    }
};

/**
 * Set Category slider to specified state
 * @param {string} categoryId - Category ID
 * @param {string} state - State from ECategorySwitches
 */
ConsentLayer.prototype.setCategorySlider = function (categoryId, state) {
    "use strict";
    document.getElementById(categoryId + "--" + state).style.display = "block";
};

/**
 *
 * @param {Event} event
 * @param {string} categoryId
 * @param {boolean} state
 */
ConsentLayer.prototype.toggleAllInCategory = function (event, categoryId, state) {
    "use strict";
    event.stopImmediatePropagation();
    event.preventDefault();

    var element = event.currentTarget;
    var elements = document.getElementsByClassName("cc-" + categoryId);
    var targetState = !!state ? ECategorySwitchStates.ENABLE_ALL : ECategorySwitchStates.DISABLE_ALL;

    element.style.display = "none";
    this.setCategorySlider(categoryId, targetState);

    for (var i = 0; i < elements.length; i++) {
        this.setCheckbox(elements[i], categoryId, state);
    }
};

/**
 * Apply consent configuration to readable format for GTM
 */
ConsentLayer.prototype.applyConsent = function () {
    "use strict";

    for (var i = 0; i < this.options.categories.length; i++) {
        for (var j = 0; j < this.options.categories[i].tagList.length; j++) {
            var name = this.options.categories[i].tagList[j].cookieName;
            var active = this.options.categories[i].tagList[j].active;

            this.writeLocalStorage(this.prefix + "_" + name, active);
        }
    }
};

/**
 * Toggle all consent settings except disabled
 * @param {boolean} state - true -> on, false -> off
 */
ConsentLayer.prototype.toggleConsent = function (state) {
    "use strict";
    for (var i = 0; i < this.options.categories.length; i++) {
        for (var j = 0; j < this.options.categories[i].tagList.length; j++) {
            var disabled = this.options.categories[i].tagList[j].disabled;

            if (!disabled) {
                this.options.categories[i].tagList[j].active = state;
            }
        }
    }
};

ConsentLayer.prototype.needReapproval = function() {
    "use strict";
    return !!this.storedConfigurationTooOld;
};

/**
 * Trigger load of consent layer
 */
ConsentLayer.prototype.triggerLoad = function () {
    "use strict";
    if (!this.isInitialized) {
        this.loadGoogleTagManager();
    }

    if (!!this.editMode) {
        window.location = this.homeUrl;
    }

    this.callback();
};

ConsentLayer.prototype.buttonAcceptSelected = function () {
    "use strict";
    this.saveOptions();
    this.closeOptionsDialog();
    this.closeConsentLayer();
    this.triggerLoad();
};

ConsentLayer.prototype.buttonAcceptAll = function () {
    "use strict";
    this.toggleConsent(true);
    this.saveOptions();
    this.closeOptionsDialog();
    this.closeConsentLayer();
    this.triggerLoad();
};

ConsentLayer.prototype.buttonAcceptMandatory = function () {
    "use strict";
    this.toggleConsent(false);
    this.saveOptions();
    this.closeOptionsDialog();
    this.closeConsentLayer();
    this.triggerLoad();
};

ConsentLayer.prototype.buttonCloseOptions = function() {
    "use strict";
    this.closeOptionsDialog();
    this.openConsentLayer();
};

/**
 * @returns {boolean}
 */
ConsentLayer.prototype.isAccepted = function() {
    "use strict";
    return this.options.hasOwnProperty("consentTime");
};

/**
 * @param {HTMLScriptElement} hElement
 */
ConsentLayer.prototype.setHeaderElement = function(hElement) {
    "use strict";

    this.hElement = hElement;
};

/**
 *
 * @param {HTMLScriptElement} hElement2
 */
ConsentLayer.prototype.setHeaderElement2 = function(hElement2) {
    "use strict";

    this.hElement2 = hElement2;
};

/**
 * @returns HTMLScriptElement
 */
ConsentLayer.prototype.getHeaderElement = function() {
    "use strict";

    return this.hElement;
};

/**
 * @returns HTMLScriptElement
 */
ConsentLayer.prototype.getHeaderElement2 = function() {
    "use strict";

    return this.hElement2;
};

/**
 * @returns {string}
 */
ConsentLayer.prototype.getOrder = function () {
    "use strict";

    var name = "orderID=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(";");
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) === " ") {
            c = c.substring(1);
        }
        if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
};

/**
 * @param {string} orderId
 */
ConsentLayer.prototype.setOrder = function(orderId) {
    "use strict";

    document.cookie ="orderID=" + orderId + ";" + "expires="+ new Date(new Date().getTime()+60*60*1000*24*365).toGMTString()+";path=/";
};

/**
 * Set the callback function to call after consent is set
 * @param callback
 */
ConsentLayer.prototype.setCallback = function(callback) {
    if (typeof(callback) === 'function') {
        this.callback = callback;
    }
}