(function ($, Drupal) {
  Drupal.behaviors.pagedesigner_init_base_components = {
    attach: function (context, settings) {
      $(document).on('pagedesigner-init-base-components', function (e, editor) {

        const defaultSettings = {
          defaults: {
            editable: false,
            draggable: false,
            droppable: false,
            badgable: false,
            stylable: true,
            highlightable: false,
            copyable: false,
            resizable: false,
            removable: false,
            hoverable: false,
            selectable: false,
          }
        };

        // lock components for editing etc.
        editor.DomComponents.getTypes().forEach(function (componentType) {
          editor.DomComponents.addType(componentType.id, {
            model: defaultSettings
          });
        });

        // basic element definition
        editor.DomComponents.addType('pd_base_element', {
          model: {

            serialize() {
              var styles = {};
              if (this.get('entityId')) {
                var selector = editor.SelectorManager.get('#pd-cp-' + this.get('entityId'));
                if (selector) {
                  editor.DeviceManager.getAll().forEach(function (device) {
                    var style = false;
                    if (device.get('widthMedia').length > 0) {
                      style = editor.CssComposer.get(selector, null, '(max-width: ' + device.get('widthMedia') + ')')
                    } else {
                      style = editor.CssComposer.get(selector);
                    }
                    if (style) {
                      styles[device.get('key')] = style.styleToString();
                    }
                  });
                }
              }

              return {
                fields: this.attributes.attributes,
                styles: styles,
                classes: this.getClasses()
              }
            },

            create() {
              if (!this.get('entityId')) {
                var component = this;
                component.beforeCreate();
                var data = {
                  pattern: component.get('type'),
                  parent: component.parent().get('entityId'),
                  container: component.getContainer(),
                  entity: parseInt(window.drupalSettings.path.nid),
                  tabIdentifier: editor.tabIdentifier
                };
                Drupal.restconsumer.post('/pagedesigner/element/', data)
                  .done(function (response) {
                    component.handleCreateResponse(response);
                    component.afterCreate();
                  }).fail(function (response) {
                    component.remove();
                    alert((response.responseJSON.message ? response.responseJSON.message : "An error occured while saving the component.") + "\nPlease reload the page and try again.");
                    editor.spinner.disable();
                  });
              }

            },

            beforeCreate() {
              // do stuff before component is created
              editor.spinner.enable();
            },

            handleCreateResponse(response) {
              this.set('entityId', parseInt(response['id']));
              this.setAttributes(Object.assign({}, this.getAttributes(), { id: 'pd-cp-' + this.get('entityId') }));
              let trait = editor.BlockManager.get(this.get('type'));
              if (trait && typeof trait != 'undefined' && trait.get('additional').remote_load) {
                let id = response['id'];
                let self = this;
                editor.spinner.enable();
                Drupal.restconsumer.get('/pagedesigner/render/' + id).done(function (response) {
                  var markup = response.filter(cmd => cmd.command == 'pd_markup')[0].data;
                  var styles = response.filter(cmd => cmd.command == 'pd_styles')[0].data;
                  self = self.replaceWith(markup);
                  editor.Parser.parseCss(styles).forEach(function (rule) {
                    if (!(Object.keys(rule.style).length === 0 && rule.style.constructor === Object)) {
                      editor.CssComposer.setIdRule(rule.selectors[0], rule.style, { mediaText: rule.mediaText })
                    }
                  });
                  editor.select(self);
                  editor.spinner.disable();
                });
              }
            },

            afterCreate() {
              this.parent().save();
              editor.select(this);
              editor.spinner.disable();
            },

            save() {
              var component = this;
              component.beforeSave();
              var data = component.serialize();
              data.tabIdentifier = editor.tabIdentifier;
              Drupal.restconsumer.patch('/pagedesigner/element/' + component.get('entityId'), data, false)
                .done(function (response) {
                  component.handleSaveResponse(response);
                }).fail(function (response) {
                  component.restore();
                  alert((response.responseJSON.message ? response.responseJSON.message : "An error occured while saving the component.") + "\nPlease reload the page and try again.");
                })
                .always(function () {
                  component.afterSave();
                  component.updateView(this, { force: true });
                });
            },

            beforeSave() {
              // do stuff before component is saved
              this.set('changed', false);
              editor.spinner.enable();
            },

            handleSaveResponse(response) {
              // do stuff with response from saving
              this.set('previousVersion', this.serialize());
              let trait = editor.BlockManager.get(this.get('type'));
              if (trait && typeof trait != 'undefined' && trait.get('additional').remote_load) {
                let id = this.get('entityId');
                let self = this;
                editor.spinner.enable();
                Drupal.restconsumer.get('/pagedesigner/render/' + id).done(function (response) {
                  var markup = response.filter(cmd => cmd.command == 'pd_markup')[0].data;
                  var styles = response.filter(cmd => cmd.command == 'pd_styles')[0].data;
                  self = self.replaceWith(markup);
                  editor.Parser.parseCss(styles).forEach(function (rule) {
                    if (!(Object.keys(rule.style).length === 0 && rule.style.constructor === Object)) {
                      editor.CssComposer.setIdRule(rule.selectors[0], rule.style, { mediaText: rule.mediaText })
                    }
                  });
                  editor.select(self);
                  editor.spinner.disable();
                });
              }
            },

            afterSave() {
              editor.spinner.disable();
              // do stuff after component is saved
            },

            load() {
              var component = this;
              component.beforeLoad();
              if (!isNaN(parseFloat(component.get('entityId'))) && isFinite(component.get('entityId'))) {
                editor.spinner.enable();
                Drupal.restconsumer.get('/pagedesigner/element/' + component.get('entityId')).done(function (response) {
                  component.handleLoadResponse(response);
                  component.afterLoad();
                });
              }

            },

            beforeLoad() {
              // clear traits
              this.get('traits').models.forEach(function (trait) {
                delete trait.attributes.value;
              })
            },

            handleLoadResponse(response) {
              this.setAttributes(Object.assign({}, this.getAttributes(), response['fields']));
              if (response['classes']) {
                this.addClass(response['classes']);
              }
              this.set('previousVersion', this.serialize());
              this.set('changed', false);
            },

            afterLoad() {
              editor.runCommand('edit-component');
              this.get('traits').models.forEach(function (trait) {
                if (trait.view && trait.view.afterInit) {
                  trait.view.afterInit();
                }
              });
              editor.spinner.disable();
            },

            restore() {
              // needs some love
              var previousData = this.get('previousVersion');
              this.setAttributes(Object.assign({}, this.getAttributes(), previousData['fields']));
              this.removeClass(this.getClasses());

              if (previousData['classes']) {
                this.addClass(previousData['classes']);
              }

              for (var media in previousData['styles']) {
                if (media == 'large') {
                  editor.CssComposer.setIdRule('pd-cp-' + this.get('entityId'), editor.Parser.parseCss('*{' + previousData['styles'][media] + '}')[0].style)
                }
                if (media == 'medium') {
                  editor.CssComposer.setIdRule('pd-cp-' + this.get('entityId'), editor.Parser.parseCss('*{' + previousData['styles'][media] + '}')[0].style, { mediaText: "(max-width: 992px)" })
                }
                if (media == 'small') {
                  editor.CssComposer.setIdRule('pd-cp-' + this.get('entityId'), editor.Parser.parseCss('*{' + previousData['styles'][media] + '}')[0].style, { mediaText: "(max-width: 768px)" })
                }
              }
              this.set('changed', false);
            },

            delete() {
              var component = this;
              var parent = this.parent();
              component.beforeDelete();
              Drupal.restconsumer.delete('/pagedesigner/element/' + component.get('entityId'))
                .done(function (response) {
                  component.handleDeleteResponse(response);
                  component.afterDelete(parent);
                }).fail(function (response) {
                  alert((response.responseJSON.message ? response.responseJSON.message : "An error occured while saving the component.") + "\nPlease reload the page and try again.");
                });
            },

            beforeDelete() {

            },

            handleDeleteResponse(response) {

            },

            afterDelete(parent) {
              if (typeof parent != 'undefined') {
                parent.save();
              }
            },

            clone() {
              var component = this;
              var data = {
                original: component.get('entityId'),
                container: component.getContainer(),
                parent: this.parent().get('entityId')
              };
              component.beforeClone();
              Drupal.restconsumer.post('/pagedesigner/clone/', data).done(function (response) {
                var component_clone = component.handleCloneResponse(response);
                component.afterClone(component_clone);
              });
            },

            beforeClone() {
              editor.spinner.enable();
            },

            handleCloneResponse(response) {
              var markup = response.filter(cmd => cmd.command == 'pd_markup')[0].data;
              var styles = response.filter(cmd => cmd.command == 'pd_styles')[0].data;
              var component_clone = this.parent().components().add(markup, { at: this.index() + 1 });
              editor.Parser.parseCss(styles).forEach(function (rule) {
                if (!(Object.keys(rule.style).length === 0 && rule.style.constructor === Object)) {
                  editor.CssComposer.setIdRule(rule.selectors[0], rule.style, { mediaText: rule.mediaText })
                }
              });
              return component_clone;
            },

            afterClone(component_clone) {
              this.parent().save();
              this.set('changed', false);
              editor.select(component_clone);
              editor.spinner.disable();
            },

            renderTwig(values) {
              var tmpl = false;
              tmpl = '{% spaceless %}' + editor.BlockManager.get(this.get('type')).get('pattern').template + '{% endspaceless %}';
              if (tmpl) {
                var twig = Twig.twig({
                  data: tmpl
                });
                return twig.render(values);
              }
            },

            getRenderData(traits) {
              var renderData = {}
              for (var trait of traits) {
                renderData[trait.get('name')] = trait.renderValue;
              }
              return renderData;
            },

            updateView(component, config) {
              if ((config.force === true || this.changed.attributes) && editor.BlockManager.get(this.get('type'))) {
                this.get('classes').models.forEach(function (classname) {
                  classname.set('active', false);
                });

                var self = this;
                window.clearTimeout(window.renderTimeout);
                window.renderTimeout = window.setTimeout(function () {
                  var block = editor.BlockManager.get(self.get('type'));
                  if (config.force === true || typeof block.attributes.additional.autorender == 'undefined' || block.attributes.additional.autorender == true) {
                    var renderData = {}
                    for (var trait of self.get('traits').models) {
                      renderData[trait.get('name')] = trait.renderValue;
                    }
                    var html = $(self.renderTwig(renderData));
                    self.components(html.html());
                  }
                }, 500);

                this.set('changed', true)
              }
            },

            getContainer() {
              return $(this.getEl()).parents('.pd-edit[data-gjs-type="container"]').attr('data-entity-id');
            },

            init() {
              this.set('entityId', parseInt(this.attributes.attributes['data-entity-id']));
              this.listenTo(this, 'change', this.updateView);
            },

            toHTML() {
              if (this.changed.attributes && this.get('template')) {
                var html = this.renderTwig(this.getRenderData(this.get('traits').models));
                return html;
              }
            },

            initToolbar(...args) {
              const { em } = this;
              const model = this;
              const ppfx = (em && em.getConfig('stylePrefix')) || '';

              if (!model.get('toolbar')) {
                var tb = [];
                if (model.get('draggable')) {
                  tb.push({
                    attributes: {
                      class: `fas fa-arrows-alt ${ppfx}no-touch-actions`,
                      title: Drupal.t('Move component'),
                      draggable: true,
                      weight: 0
                    },
                    //events: hasDnd(this.em) ? { dragstart: 'execCommand' } : '',
                    command: 'tlb-move'
                  });
                }
                if (model.collection) {
                  tb.push({
                    attributes: {
                      class: 'fas fa-arrow-up',
                      title: Drupal.t('Select parent'),
                      weight: 1
                    },
                    command: 'select-parent',
                  });
                }
                if (model.get('copyable')) {
                  tb.push({
                    attributes: {
                      class: 'far fa-copy',
                      title: Drupal.t('Copy component'),
                      weight: 2
                    },
                    command: 'tlb-copy-component'
                  });
                }
                if (model.get('removable')) {
                  tb.push({
                    attributes: {
                      class: 'far fa-trash-alt',
                      title: Drupal.t('Delete component'),
                      weight: 20
                    },
                    command: 'tlb-delete-component'
                  });
                }

                tb.push({
                  attributes: {
                    class: 'fas fa-times',
                    title: Drupal.t('Close component'),
                    weight: 30
                  },
                  command: 'restore-component'
                });

                model.set('toolbar', tb);
              }

            },

            setStylableProperties() {
              var styles = [];
              var component_stylable = this.get('styles');

              if (Object.keys(component_stylable).length > 0) {
                if (component_stylable.groups) {
                  var properties = component_stylable.groups.map(group => editor.StyleManager.getProperties(group).models.map(property => {
                    var properties = [property.get('property')];
                    if (property.get('type') == 'composite' || property.get('type') == 'stack') {
                      properties = properties.concat(property.get('properties').models.map(subProperty => subProperty.get('property')));
                    }
                    return properties;
                  }));
                  styles = styles.concat(properties.concat.apply([], properties.concat.apply([], properties)));
                }
                if (component_stylable.properties) {
                  styles = styles.concat(component_stylable.properties);
                }
                this.set('styles', false);
                this.set('stylable', styles);
                this.set('changed', false);
              }
            }
          },
          isComponent(element) {
            if (element.nodeType != 3) {
              return {
                type: 'default'
              }
            }
          }
        });

        // define base twig component
        editor.DomComponents.addType('component', {
          extend: 'pd_base_element',
          model: {
            defaults: {
              editable: true,
              badgable: true,
              selectable: true,
              highlightable: true,
              hoverable: true,
              removable: true,
              copyable: true,
              draggable: '[data-grapes-block="content"] [data-gjs-type="cell"]',
            },
            handleCreateResponse(response) {
              editor.DomComponents.getType('pd_base_element').model.prototype.handleCreateResponse.apply(this, [response]);
              var count = 0;
              if (response.fields) {
                var cells = response.fields.filter(field => { return field.type === 'cell' });
                this.find('[data-gjs-type="cell"]').forEach(function (cell) {
                  cell.set('entityId', parseInt(cells[count].id));
                  cell.setAttributes(Object.assign({}, cell.getAttributes(), { id: 'pd-cp-' + cell.get('entityId'), 'data-entity-id': cell.get('entityId') }));
                  count++;
                });
              }
              this.set('changed', false);
            },
            init() {
              this.set('entityId', parseInt(this.attributes.attributes['data-entity-id']));
              if (!this.components().length && this.get('template')) {
                var renderData = editor.BlockManager.get(this.attributes.type).attributes.preview;
                for (var key in renderData) {
                  if (!renderData[key]) {
                    renderData[key] = '<div data-gjs-type="cell"></div>'
                  }
                }
                this.components(this.renderTwig(renderData));
              }
              this.listenTo(this, 'change', this.updateView);
            },
          }
        });

        // define row
        editor.DomComponents.addType('row', {
          extend: 'component',
          model: {
            defaults: {
              draggable: '[data-grapes-block="content"], [data-grapes-block="content"] [data-gjs-type="cell"]'
            },
            updateView(component, config) {
              if (this.changed.attributes) {
                this.set('changed', true)
              }
            },
          }
        });

        // define cells
        editor.DomComponents.addType('cell', {
          extend: 'pd_base_element',
          model: {
            defaults: {
              droppable: true,
              badgable: true,
              hoverable: true
            },
            serialize() {
              var children = [];
              this.components().models.forEach(function (child) {
                if (child.get('entityId')) {
                  children.push(child.get('entityId'));
                }
              });
              return {
                order: children
              }
            },
            load() {
              // dont do anything
            },
            create() {
              // dont do anything
            }
          }
        });

        // define cells
        editor.DomComponents.addType('container', {
          extend: 'cell'
        });
      });
    }
  };
})(jQuery, Drupal);
