Vue.component('click-by-click-assistant', {
    props: ['pages', 'allowclose', 'pagination'],
    data: function(){
        return {
            activePage: 0,
            currentTransition:'',
            dataStorage: []
        }
    },
    template: '<div class="click-by-click-assistant"><div class="wrapper"><div class="container">' +
        '<div v-if="allowclose" class="app-close-button" @click="$emit(\'close\')"></div>' +

        '<transition :name="currentTransition" mode="out-in">' +

            /** DEFAULT TEXT PAGE **/
            '<div class="page" v-for="(page, index) in pages" ' +
                    'v-if="page.type === \'defaultPage\' && activePage === index" ' +
                    ':data-pageIndex="index" ' +
                    ':key="index">' +
                '<app-media v-if="page.headerMedia" :media="page.headerMedia"></app-media>' +
                '<div class="page-content">' +
                    '<div v-if="!page.headerMedia && page.icon" class="header-icon" :class="page.icon"></div>' +
                    '<h2 v-if="page.headline" v-html="page.headline"></h2>'+
                    '<h3 v-if="page.subHeadline" v-html="page.subHeadline"></h3>'+
                    '<p class="page-text" v-if="page.text" v-html="page.text"></p>'+
                    '<div class="flex-container" v-if="page.link">'+
                        '<div v-if="page.link" class="link">'+
                           '<a class="link" :href="page.link.link" >{{ page.link.title }}</a>'+
                       '</div>' +
                    '</div>'+
                    '<button v-if="button.action === \'next\'" ' +
                        'v-for="button in page.ctaButtons" ' +
                        'class="button button-primary cta center" ' +
                        '@click="changePage(\'next\')">{{ button.title }}</button>'+

                    '<button v-if="!button.link && button.action === \'close\'" ' +
                        'v-for="button in page.ctaButtons" ' +
                        'class="button button-primary cta center" ' +
                        '@click="$emit(\'close\')">{{ button.title }}</button>'+

                    '<button v-if="!button.link && button.action === \'completeStep\'" ' +
                        'v-for="button in page.ctaButtons" ' +
                        'class="button button-primary cta center" ' +
                        '@click="$emit(\'completeStep\')">{{ button.title }}</button>'+

                    '<button v-if="button.link && button.action === \'close\'" ' +
                        'v-for="button in page.ctaButtons" ' +
                        'class="button button-primary cta center" ' +
                        '@click="link(button.link)">{{ button.title }}</button>'+

                    '<app-pagination v-if="pagination" :pages="pages" :index="index"></app-pagination>' +
                '</div>'+
            '</div>' +

            /** FORM PAGE **/
            '<div class="page" v-for="(page, index) in pages" ' +
                'v-if="(page.type === \'form\' || page.type === \'survey\') && activePage === index" ' +
                ':data-pageIndex="index" :key="index">' +
                '<app-media v-if="page.headerMedia" :media="page.headerMedia"></app-media>' +
                '<div class="page-content">' +
                    '<div v-if="!page.headerMedia && page.icon" class="header-icon" :class="page.icon"></div>' +
                    '<h2 v-html="page.headline"></h2>'+
                    '<p v-if="page.subHeadline" v-html="page.subHeadline"></p>'+
                    '<app-form :page="page"></app-form>' +
                    '<app-pagination v-if="pagination" :pages="pages" :index="index"></app-pagination>' +
                '</div>' +
            '</div>' +
        '</transition>' +

        '</div></div></div>',
    mounted: function(){
        var self = this;
        self.saveDataRequiredForSubmit();

        self.$on('completeStep', function(){

            if(WizardContainer !== undefined){
                WizardContainer.completeStep();
            }

            self.$emit('close');
        });
    },
    methods: {
        /**
         * @param {string} direction
         */
        changePage: function(direction){
            if(direction !== 'back' && direction !== 'next'){
                return;
            }

            this.activePage = direction === 'next' ? this.activePage + 1 : this.activePage - 1;
            this.currentTransition = direction;
        },
        link: function(link)
        {
            window.location.href = link;
        },

        /**
         * saves all data that was defined on the building JSON file in order to submit it later
         */
        saveDataRequiredForSubmit: function(){
            var current;

            for(var i = 0; i < this.pages.length; i++){
                current = this.pages[i].dataRequiredForSubmit;

                if(current === undefined || current.length === 0){
                    return;
                }
                this.setToStorage(current);
            }
        },

        /**
         * @param {Object} object
         */
        setToStorage: function(object){
            this.dataStorage.push(object);
        },

        /**
         *
         * @returns {Object}
         */
        getStorage: function(){
            return this.dataStorage;
        },

        /**
         * clear storage, but keeps vue listener on this.dataStorage
         */
        clearStorage: function(){
            for (var member in this.dataStorage) {
                delete this.dataStorage[member];
            }
        }
    }
});

Vue.component('app-form',{
   props: ['page'],
    data: function () {
        return {
            rowId: 0,
            surveyChoice: [],
            showSurveyError: false,
            formValid: true,
            formWasValidated: false,
            loading: false,
            errorMsg: 'Bitte überprüfe die Eingabefelder'
        }
    },
    template:
    '<form @submit.prevent="processForm" novalidate>' +
        '<div class="flex-container" v-for="(row, rowIndex) in page.form" :key="row.id">' +
            '<app-input-row v-if="row.inputs !== undefined && row.inputs.length > 0" ' +
                    'ref="row" :row="row" ' +
                    ':hasSiblings="page.form.length > 1" ' +
                    '@deleteme="removeInputRow(rowIndex)"></app-input-row>' +

            '<span v-else-if="row.surveyButtons !== undefined && row.surveyButtons.length > 0" ' +
                    'class="survey-button-container" ' +
                    'v-for="button in row.surveyButtons">'+
                '<input type="checkbox" :id="button.value" name="data" :value="button.value" v-model="surveyChoice"/>'+
                '<label :for="button.value" class="button button-secondary" > {{ button.title }} </label>' +
            '</span>' +
        '</div>' +
        '<div class="flex-container" v-if="page.link">'+
            '<div v-if="page.link" class="add-row">'+
                '<a class="link" :href="page.link.link" >{{ page.link.title }}</a>'+
            '</div>' +
        '</div>'+
        '<transition name="fade">' +
            '<div v-if="page.errorMsg && showSurveyError && surveyChoice.length === 0" ' +
                    'class="errorMsg">{{ page.errorMsg }}</div>'+
            '<div v-if="formWasValidated && !formValid" class="errorMsg"> {{ errorMsg }}</div>'+
        '</transition>' +
        '<button v-for="button in page.ctaButtons" ' +
            ':type="button.action" class="button button-primary cta center">{{ button.title }}' +
        '<app-spinner v-if="loading"></app-spinner></button>' +
        '</form>',
    methods:{
        /**
         * @param {Object} row
         */
        addInputRow: function(row){
            this.rowId++
            row.id = this.rowId;

            for(var k = 0; k < row.inputs.length; k++){
                row.inputs[k].name += this.rowId;
            }

            this.page.form.push(row);

            for(var i = 0; i < this.page.form.length -1; i++){
                this.page.form[i].add.allow = false;
            }

            if(row.add.maximum === this.page.form.length){
                this.allowAddOnLastRow(false);
            }
        },

        /**
         * @param {number} index
         */
        removeInputRow: function(index){
            if(this.page.form.length <= 1){
                return;
            }

            this.page.form.splice(index, 1);

            this.allowAddOnLastRow(true);
        },

        /**
         * @param {string} decision
         */
        allowAddOnLastRow: function(decision){
            var lastIndex =  this.page.form.length - 1;
            this.page.form[lastIndex].add.allow = decision;
        },

        /**
         * @param {Object} e
         */
        processForm: function(e){
            this.validateForm();

            if(!this.formValid){
                return;
            }

            if(!this.page.submitType){
                throw new Error("Please define submitType in your JSON");
            }

            if(this.page.submitType === "save"){
                this.$parent.setToStorage(this.filterDataFromSubmitEvent(e));
                this.$parent.changePage("next");

                return;
            }

            this.submitForm(e);
        },

        validateForm: function(){
            if(this.page.submitType === 'survey'){
                this.formValid = this.surveyChoice.length !== 0;
                this.formWasValidated = true;

                if(!this.formValid){
                    this.showSurveyError = true;
                }
            } else {
                this.formValid = this.requiredRowsValid();
            }
        },

        /**
         * Checks if the required rows are valid
         * @returns {boolean}
         */
        requiredRowsValid: function(){
            if(this.$refs === undefined){
                console.error("Please define ref on child component");
                return false;
            }

            if(this.$refs.row === undefined) {
                return true;
            }

            var current;

            for(var i = 0; i < this.$refs.row.length; i++){
                current = this.$refs.row[i];

                // case 1: if row has not been validated (no user input), form is valid in regard of this row
                if(!current.rowWasValidated){
                    this.formWasValidated = false;
                    return true;
                }

                // case 2: if row is invalid, form is not valid
                // rowValid only includes required inputs (filtered out on row component)
                if(!current.rowValid){
                    this.formWasValidated = true;
                    return false;
                }
            }

            return true; // if case1 or case2 didn't match, form valid
        },

        /**
         * @param {Event} e
         */
        submitForm: function(e){
            var request = new XMLHttpRequest(),
                self = this,
                data,
                responseJson;

            data = this.prepareSubmitData(e);

            request.open("POST", this.page.submitUrl + '', true);

            request.addEventListener('load', function(event) {
                if (request.status >= 200 && request.status < 300) {
                    console.log("POST " + request.statusText + " status: " + request.status);
                    responseJson = JSON.parse(request.responseText);
                    if(responseJson.page !== undefined) {
                        self.$parent.pages.push(responseJson.page);
                    }
                    self.$parent.clearStorage();
                    if(responseJson.dataRequiredForSubmit !== undefined){
                        self.$parent.setToStorage(responseJson.dataRequiredForSubmit);
                    }

                    self.$parent.changePage("next");

                    self.loading = false;
                } else {
                    console.warn(request.statusText, request.responseText);

                    self.loading = false;
                    self.formValid = false;
                    self.formWasValidated = true;

                    responseJson = JSON.parse(request.responseText);

                    if(responseJson.error !== undefined) {
                        self.errorMsg = responseJson.error;
                    }
                    else {
                        self.errorMsg = 'Ooops, da ist etwas schief gelaufen. Bitte versuche es erneut.';
                    }

                    if(responseJson.dataRequiredForSubmit !== undefined){
                        self.$parent.setToStorage(responseJson.dataRequiredForSubmit);
                    }
                }
            });
            self.loading = true;
            request.send(data);
        },

        /**
         * Combines all available data of all not-submitted pages
         *
         * @param {Object} e
         *
         * @returns {FormData}
         */
        prepareSubmitData: function(e){
            var submitData = new FormData(),
                filteredEventData,
                storageData;

            filteredEventData = this.filterDataFromSubmitEvent(e);
            storageData = JSON.parse(JSON.stringify(this.$parent.getStorage()));
            if(storageData !== undefined && storageData.length > 0){
                for(var i = 0; i < storageData.length; i++){

                    for(var key in storageData[i]){
                        submitData.append(key, storageData[i][key]);
                    }
                }
            }
            if(filteredEventData !== undefined){
                for(var filteredEventDataKey in filteredEventData){
                    submitData.append(filteredEventDataKey, filteredEventData[filteredEventDataKey]);
                }
            }
            return submitData;
        },

        /**
         * Serializes all data from a form submit event
         *
         * @param e
         *
         * @returns {Object}
         */
        filterDataFromSubmitEvent: function(e){
            var data = {},
                checkedInSurvey = [],
                current;

            for(var i = 0; i < e.target.length; i++){
                current = e.target[i];

                if(current.tagName === "button" || current.tagName === "BUTTON"){
                    continue;
                }

                if(!current.name){
                    throw new Error("Please define names for all inputs");
                }

                if(this.page.type === "survey" && (current.tagName === "input" || current.tagName === "INPUT")){
                    if(current.checked){
                        checkedInSurvey.push(current.value);
                    }

                    data[current.name] = checkedInSurvey;
                } else {
                    if(current.type === 'checkbox') {
                        if(current.checked){
                            data[current.name] = current.value;
                        }
                    }
                    else {
                        data[current.name] = current.value;
                    }
                }
            }

            return data;
        }
    }
});

Vue.component('app-input-row',{
    props: ['row', 'hasSiblings'],
    data: function(){
      return {
          rowValid: true,
          rowWasValidated: false
      }
    },
    template:
        '<div class="app-row-container">' +
            '<div class="app-input-row" :class="{\'reduced-width\': row.inputs.length === 1  }">' +
                '<div class="app-row-valid" :class="{\'icon-ok\': rowValid && rowWasValidated}"></div>' +
                '<app-input ' +
                'v-for="(input, inputIndex) in row.inputs" ' +
                ':type="input.type" ' +
                ':validation="input.validation" ' +
                ':customErrorMsg="input.customErrorMsg" ' +
                ':name="input.name" ' +
                ':label="input.label"' +
                ':value="input.value"' +
                ':connectedTo="input.connectedTo"' +
                ':options="input.options"' +
                'ref="input"'+
                ':key="inputIndex"></app-input>' +
                '<div v-if="row.removable && hasSiblings" @click="$emit(\'deleteme\')" class="remove-row"></div>' +
            '</div>' +
            '<div v-if="row.add && row.add.allow" @click="addRow" class="add-row">{{ row.add.text }}</div>' +
            '<div v-if="row.link" class="add-row"><a class="link" :href="row.link.link" >{{ row.link.title }}</a></div>' +
        '</div>',
    methods:{
        addRow: function(){
            var newRow = JSON.parse(JSON.stringify(this.row)); // removes vue observable and makes it possible to change

            this.$parent.addInputRow(newRow);
        },

        validateRow: function(){
            this.rowValid = this.requiredInputsValid();

            this.rowWasValidated = true;

            if(this.rowValid){
                this.$parent.validateForm();
            }
        },

        /**
         * Checks if the required Inputs are valid
         *
         * @returns {boolean}
         */
        requiredInputsValid: function(){
            if(this.$refs === undefined){
                throw new Error("Please define ref on child component");
            }

            var valid = true,
                current;

            for(var i = 0; i < this.$refs.input.length; i++){
                current = this.$refs.input[i];

                if(!current.validation){
                    valid = true;
                    continue;
                }

                valid = current.validation && current.valid && current.wasValidated;

                // row is invalid after first invalid input
                if(!valid){
                    return false;
                }
            }

            return valid;
        }
    }
});

Vue.component('app-input', {
    props: ['type', 'validation', 'name', 'label', 'customErrorMsg', 'options', 'value', 'connectedTo'],
    data: function () {
        return {
            inputValue: this.value ? this.value : '',
            inputType: this.type,
            inputErrorMsg: undefined,
            valid: true,
            wasValidated: false
        }
    },
    template:
        '<div v-if="type === \'select\'" class="app-input select" :class="{\'input-error\': !valid }">' +
            '<select :id="name" :name="name" v-model="inputValue" :class="{\'hasSelected\': inputValue.length > 0  }" ' +
                    '@change="validateInput" >' +
            '<option v-for="(option, index) in options" :value="option.value" :key="index">{{ option.text }}</option>' +
            '</select>' +
            '<label :for="name">{{ label }} <span v-if="validation"> (Pflichtfeld)</span></label>' +
        '</div>'+

        '<div v-else class="app-input" :class="{\'input-error\': !valid}">' +
            //'<input style="display: none" type="password" />' +
            '<input :type="inputType" :id="name" :name="name" v-model="inputValue" ' +
                    ':class="{\'hasValue\': inputValue.length > 0  }" ' +
                    '@blur="validateInput" autocomplete="off" required />' +
            '<div v-if="type === \'password\'" class="reveal" @click="togglePasswordVisibility"></div>' +
            '<label :for="name">{{ label }} <span v-if="validation"> (Pflichtfeld)</span></label>' +
            '<transition name="fade">' +
                '<div v-if="!valid && inputErrorMsg" class="input-error"> {{ inputErrorMsg }}</div>' +
            '</transition>'+
        '</div>',
    mounted: function(){
        var self = this;

        // listens to compare request "broadcast" from other component
        self.$root.$on('compareConnected', function(data){
          if(self.name !== data.connectedTo) {
              return;
          }

          // "broadcasts" to every component listening
          self.$root.$emit('comparisonResult', {
              requestingInput: data.requestingInput.name,
              valid: self.inputValue === data.requestingInput.inputValue && self.valid
          })
        });

        self.$root.$on('comparisonResult', function(result){
            if(self.name === result.requestingInput){
                self.valid = result.valid;
            }
        });
    },
    methods: {
        validateInput: function(){
            if((this.inputValue.length === 0 && !this.wasValidated) || !this.validation){
                // input is valid if it has a value and wasn't validated before (inputs do not get validated on page render)
                // or if it's not necessary to validate
                this.valid = true;
                return;
            }

            switch(this.type){
                case "email":
                    var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
                    this.valid = regex.test(this.inputValue);
                    this.inputErrorMsg = this.customErrorMsg || "Adresse nicht gültig";
                    break;

                case "text":
                    this.valid = this.inputValue.length >= 2;
                    this.inputErrorMsg = this.customErrorMsg || "Mindestens zwei Zeichen";
                    break;

                case "password":

                    // "broadcasting" event to listening components
                    // In this case we compare if passwords match in connected fields -> "connectedTo" option in JSON
                    if(this.connectedTo !== undefined){
                        this.$root.$emit('compareConnected', {
                            connectedTo: this.connectedTo,
                            requestingInput: {
                                name: this.name,
                                inputValue: this.inputValue
                            }
                        });
                        this.inputErrorMsg = this.customErrorMsg || "Bitte wiederholen Sie das Passwort";

                    } else {
                        this.valid = this.inputValue.length >= 4;
                        this.inputErrorMsg = this.customErrorMsg || "Mindestens vier Zeichen";
                    }

                    break;

                case "select":
                    // it's "selected/changed" (event) so it always has a valid value
                    break;

                default:
                    break;
            }

            this.wasValidated = true;

            this.$parent.validateRow();
        },

        togglePasswordVisibility: function(){
            this.inputType = this.inputType === 'password' ? 'text' : 'password';
        }
    }
});

Vue.component('app-pagination', {
    props: ["pages", "index"],
    template: '' +
        '<div class="app-pagination">' +
            '<div v-for="(dot, dotIndex) in pages" :class="{\'active\': index === dotIndex}"></div>' +
        '</div>'
});

Vue.component('app-spinner',{
    template: '<div class="spinner spinner-circle"></div>'
});

Vue.component('app-media',{
    props: ["media"],
    template:
        '<div>' +
            '<iframe v-if="media.type === \'video\'"' +
                'class="media-youtube" ' +
                ':src="media.link + \'?rel=0\'" ' +
                'frameborder="0" ' +
                'allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" ' +
                'allowfullscreen>' +
            '</iframe>' +

            '<img ' +
                'v-if="media.type === \'image\'"' +
                'class="media-image"' +
                ':src="media.link">' +
            '<img/>' +
        '</div>'
});