/**
 * i-doit category folders tree class.
 */
window.CategoryFoldersTree = Class.create({
    options:    {},
    initialize: function ($container, options) {
        this.$container = $container;
        this.options = {
            $toggleAll: null,
            $searchCategoryField: null,
            selectable: false,
            selection: null,
            addable: false,
            editable: false,
            movable: false,
            deletable: false,
            $scrollContainer: null,
            preventSelectionOf: null,
            afterRender: Prototype.emptyFunction
        };

        this.configurationId = null;
        this.objectTypeId = null;
        this.data = { folders: [], categories: [] };
        this.openFolders = [];
        this.$emptyFolderHtml = new Element('p', { className: 'pt20 text-neutral-600' }).update(idoit.Translate.get('LC__CATEGORY_FOLDERS__FOLDER_EMPTY')).outerHTML;

        Object.extend(this.options, options || {});

        if (!Array.isArray(this.options.preventSelectionOf)) {
            this.options.preventSelectionOf = [this.options.preventSelectionOf];
        }

        this.$container.on('click', '.folder .toggle-btn', function (ev) {
            const $button = ev.findElement('button');

            if (!$button) {
                return;
            }

            const $folderContainer = $button.up('.folder-container');

            if (!$folderContainer) {
                return;
            }

            const $childContainer = $folderContainer.down('.folder-children');
            const folderId = +$folderContainer.readAttribute('data-id');

            if (!$childContainer) {
                return;
            }

            if ($childContainer.toggleClassName('hide').hasClassName('hide')) {
                $button.down('img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-right-small.svg');
                this.openFolders = this.openFolders.without(folderId);
            } else {
                $button.down('img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-down-small.svg');
                this.openFolders.push(folderId);
            }
        }.bind(this));

        if (this.options.$toggleAll) {
            this.options.$toggleAll.on('click', function () {
                const action = this.options.$toggleAll.readAttribute('data-action');

                if (action === 'expand') {
                    this.options.$toggleAll.writeAttribute('data-action', 'collapse')
                        .down('img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-down-small.svg')
                        .next('span').update(idoit.Translate.get('LC__CATEGORY_FOLDERS__COLLAPSE_ALL'));

                    this.$container.select('.folder .toggle-btn img').invoke('writeAttribute', 'src', window.dir_images + 'axialis/user-interface/angle-down-small.svg');
                    this.$container.select('.folder-children').invoke('removeClassName', 'hide');
                } else {
                    this.options.$toggleAll.writeAttribute('data-action', 'expand')
                        .down('img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-right-small.svg')
                        .next('span').update(idoit.Translate.get('LC__CATEGORY_FOLDERS__EXPAND_ALL'));

                    this.$container.select('.folder .toggle-btn img').invoke('writeAttribute', 'src', window.dir_images + 'axialis/user-interface/angle-right-small.svg');
                    this.$container.select('.folder-children').invoke('addClassName', 'hide');
                }
            }.bind(this));
        }

        if (this.options.$searchCategoryField) {
            preventAccidentalFormSubmit(this.options.$searchCategoryField);

            this.options.$searchCategoryField.on('keyup', 'input', function () {
                delay(this.categorySearch.bind(this), 200);
            }.bind(this));

            this.options.$searchCategoryField.on('click', 'img', function (ev) {
                if (!ev.findElement('img').hasClassName('mouse-pointer')) {
                    return;
                }

                this.options.$searchCategoryField.down('input').setValue('');
                this.categorySearch();
            }.bind(this));
        }

        // @see ID-9768 Make the whole item clickable.
        if (this.options.selectable) {
            this.$container.on('click', '.folder', function (ev) {
                const $clickedElement = ev.findElement();

                // Don't select the folder, if the button or radio button was clicked.
                if ($clickedElement.up('button') || $clickedElement.tagName.toLowerCase() === 'button' || $clickedElement.tagName.toLowerCase() === 'input') {
                    return;
                }

                const $radio = $clickedElement.up('.folder-container').down('[type="radio"]');

                // @see ID-10003 Do not select disabled radios.
                if ($radio && !$radio.disabled) {
                    $radio.setValue(1);
                }
            });
        }

        if (this.options.editable) {
            this.$container.on('click', '[data-action="edit"]', function (ev) {
                const itemId = ev.findElement('button').up('[data-id]').readAttribute('data-id');

                Modal.openLegacy({
                    popupType: 'category_folders_edit',
                    maxWidth: 550,
                    maxHeight: 300,
                    parameters: {
                        static: {
                            configurationId: this.configurationId,
                            objectTypeId:    this.objectTypeId,
                            itemId:          itemId
                        }
                    }
                });
            }.bind(this))
        }

        if (this.options.addable) {
            this.$container.on('click', '[data-action="add"]', function (ev) {
                const itemId = ev.findElement('button').up('[data-id]').readAttribute('data-id');

                Modal.openLegacy({
                    popupType:  'category_folders_create',
                    maxWidth:   550,
                    maxHeight:  300,
                    parameters: {
                        static: {
                            configId: this.configurationId,
                            parent:   itemId
                        }
                    }
                });
            }.bind(this))
        }

        if (this.options.deletable) {
            this.$container.on('click', '[data-action="delete"]', function (ev) {
                const folderId = ev.findElement('button').up('[data-id]').readAttribute('data-id');

                Modal.openLegacy({
                    popupType: 'category_folders_delete',
                    maxWidth: 550,
                    maxHeight: 300,
                    parameters: {
                        static: {
                            configurationId: this.configurationId,
                            folderId:        folderId
                        }
                    }
                });
            }.bind(this))
        }

        if (this.options.movable) {
            this.$container.on('click', '[data-action="move"]', function (ev) {
                const $item = ev.findElement('button').up('[data-id]');
                const parentId = $item.up('.folder-container')
                    ? +$item.up('.folder-container').readAttribute('data-id')
                    : +this.root.id;
                const itemId = $item.readAttribute('data-id');
                const itemType = $item.hasClassName('folder-container') ? 'folder' : 'category';

                Modal.openLegacy({
                    popupType:  'category_folders_move',
                    maxWidth:   720,
                    maxHeight:  600,
                    parameters: {
                        static: {
                            configurationId: this.configurationId,
                            objectTypeId:    this.objectTypeId,
                            itemType:        itemType,
                            itemId:          itemId,
                            parentId:        parentId
                        }
                    },
                    minWidth:   550,
                    minHeight:  300
                });
            }.bind(this))
        }

        return this;
    },

    categorySearch: function () {
        const $items = this.$container.select('.category,.folder');
        const $childContainer = this.$container.select('.folder-children');
        const searchTerm = this.options.$searchCategoryField.down('input').getValue().toLowerCase().trim();

        if (this.$container.down('.empty-notification')) {
            this.$container.down('.empty-notification').remove();
        }

        if (searchTerm.blank()) {
            this.options.$searchCategoryField.down('img')
                .removeClassName('mouse-pointer')
                .writeAttribute('src', window.dir_images + 'axialis/basic/zoom.svg');

            // @see ID-10089 Set the data based on the current DOM before re-rendering.
            this.rebuildData();

            // @see ID-10071 Simply re-render the tree - this will also reopen previously opened folders (see ID-10067)
            this.render();
            return;
        }

        this.options.$searchCategoryField.down('img')
            .addClassName('mouse-pointer')
            .writeAttribute('src', window.dir_images + 'axialis/basic/symbol-cancel-outline.svg');

        // @see ID-10088 Save the scroll-top, before we hide all elements (thereby scrolling up the modal content).
        const tmpScrollTop = this.options.$scrollContainer ? this.options.$scrollContainer.scrollTop : 0;

        $items.invoke('addClassName', 'hide');
        $childContainer.invoke('addClassName', 'hide').invoke('hide');
        this.$container.select('button.toggle-btn img').invoke('writeAttribute', 'src', window.dir_images + 'axialis/user-interface/angle-right-small.svg');

        $items
            .filter(($item) => $item.down('span').innerText.toLowerCase().includes(searchTerm))
            .map(($item) => {
                // Use 'show / hide' to make it look properly when expanding / collapsing all.
                let $node = $item.removeClassName('hide');

                if ($node.hasClassName('folder')) {
                    // Use the proper icon for the 'toggle' button.
                    $node.down('button.toggle-btn img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-down-small.svg');

                    // If the found node is a folder, show all of its contents.
                    $node.next('.folder-children').select('.folder,.category').invoke('removeClassName', 'hide');
                }

                // Show the path to the found node.
                while ($node.up('.folder-children')) {
                    // Use 'show / hide' to make it look properly when expanding / collapsing all.
                    $node = $node.up('.folder-children').show().removeClassName('hide').previous('.folder').removeClassName('hide');

                    // Use the proper icon for the 'toggle' button.
                    $node.down('button.toggle-btn img').writeAttribute('src', window.dir_images + 'axialis/user-interface/angle-down-small.svg');
                }
            });

        // @see ID-10088 Re-set the scroll-top.
        if (this.options.$scrollContainer) {
            this.options.$scrollContainer.scrollTop = tmpScrollTop;
        }

        if ($items.filter(($item) => !$item.hasClassName('hide')).length === 0) {
            this.$container
                .insert(new Element('div', { className: 'empty-notification category' })
                    .update(new Element('span', { className: 'ml20 text-neutral-600' }).update(idoit.Translate.get('LC__CATEGORY_FOLDERS__NO_SEARCH_RESULTS'))));
        }
    },

    setData: function (data) {
        this.data = data;

        this.root = this.data.find((item) => item.parent === null);
    },

    setConfigurationId: function (configurationId) {
        this.configurationId = configurationId;
    },

    setObjectTypeId: function (objectTypeId) {
        this.objectTypeId = objectTypeId;
    },

    getRawData: function () {
        // Get the list of items to apply the current order.
        const $itemsList = this.$container.select('.category,.folder');

        // Return a copy of our data, also add the 'order'.
        return [...this.data]
            .map((item) => {
                const upCounter = item.isCategory ? 0 : 1;
                // Use the actual index of the current item, add one to start with 0 (for root).
                item.order = $itemsList.indexOf(item.node) + 1;

                if (item.node) {
                    if (item.node.up('.folder-container', upCounter)) {
                        const newParent = +item.node.up('.folder-container', upCounter).readAttribute('data-id');

                        // When the structure gets rebuilt, the IDs are all zero.
                        if (newParent > 0 && item.parent !== newParent) {
                            item.parent = newParent;
                        }
                    } else {
                        item.parent = this.root.id;
                    }
                }

                // Delete the nodes when exporting data.
                delete item.node;
                delete item.childContainer;

                return item;
            })
            .sort((a, b) => a.order - b.order);
    },

    getData: function () {
        const dataCopy = this.getRawData();

        return {
            folders:    dataCopy
                .filter((item) => item.isFolder)
                .map((folder) => {
                    delete folder.type;
                    delete folder.isCategory;
                    delete folder.isFolder;

                    return folder;
                }),
            categories: dataCopy
                .filter((item) => item.isCategory)
                .map((category) => {
                    delete category.name;
                    delete category.isCategory;
                    delete category.isFolder;

                    return category;
                })
        }
    },

    rebuildData: function () {
        this.setData(this.getRawData());
    },

    render: function () {
        this.$container.removeClassName('selectable').update();

        if (this.options.selectable) {
            this.$container.addClassName('selectable');

            this.$container.insert(this.folderNode(this.root, false));
        }

        if (this.options.$toggleAll) {
            this.options.$toggleAll.removeClassName('hide');
        }

        if (this.options.$searchCategoryField) {
            this.options.$searchCategoryField.removeClassName('hide');
        }

        this.renderByParent(this.root.id, this.options.preventSelectionOf.includes(this.root.id));

        // Refresh tooltips.
        $('body').fire('update:tooltips');

        this.options.afterRender();
    },

    renderByParent: function (parent, disableSelection) {
        const isRoot = parent === this.root.id;
        const children = this.data.filter((item) => item.parent === parent);
        const potentialParent = this.data.find((item) => item.isFolder && item.id === parent);
        const $parent = (!isRoot && potentialParent && potentialParent.hasOwnProperty('childContainer')) ? potentialParent.childContainer : this.$container;

        if (children.length === 0) {
            $parent.insert(this.$emptyFolderHtml);
            return;
        }

        for (let i in children) {
            if (!children.hasOwnProperty(i)) {
                continue;
            }

            if (children[i].isFolder) {
                $parent.insert(this.folderNode(children[i], disableSelection || this.options.preventSelectionOf.includes(children[i].id)));

                // If the current folder is selected, we'll open all the parents.
                if (this.options.selection === children[i].id) {
                    let $directParent = $parent;

                    while ($directParent = $directParent.up('.folder-children')) {
                        $directParent.previous('.folder').down('.toggle-btn').simulate('click');
                    }
                }

                this.renderByParent(children[i].id, disableSelection || this.options.preventSelectionOf.includes(children[i].id));
            }

            if (children[i].isCategory) {
                // In the "select" mode, skip the categories assigned to the root, they will be rendered separately.
                if (this.options.selectable && isRoot) {
                    this.root.childContainer.insert(this.categoryNode(children[i]));
                } else {
                    $parent.insert(this.categoryNode(children[i]));
                }
            }
        }
    },

    folderNode: function (folder, disableSelection) {
        const $folderContainer = new Element('div', { className: 'folder-container', 'data-id': folder.rawId });
        const $folder = new Element('div', { className: 'folder' });
        const $selectable = this.options.selectable
            ? new Element('input', { type: 'radio', name: 'selectedFolder', value: folder.id, checked: this.options.selection === folder.id, disabled: !!disableSelection })
            : '';

        // @see ID-10067 Show the correct toggle icon, when a folder is opened.
        const toggleImage = this.openFolders.includes(folder.id)
            ? window.dir_images + '/axialis/user-interface/angle-down-small.svg'
            : window.dir_images + '/axialis/user-interface/angle-right-small.svg'

        $folder.insert($selectable);
        $folder.insert(new Element('button', {
            type:      'button',
            className: 'btn btn-secondary toggle-btn mr10'
        }).update(new Element('img', {
            src: toggleImage,
            alt: 'toggle'
        })));
        $folder.insert(new Element('img', { src: window.dir_images + '/axialis/documents-folders/' + (folder.id === this.root.id ? 'folder-forbidden.svg' : 'folder-filled.svg'), className: 'mr5', alt: '' }));

        const $folderNameSpan = new Element('span');
        // @see ID-10094 Use 'innerText' in order to write TEXT and not risk XSS.
        $folderNameSpan.innerText = folder.name;
        $folder.insert($folderNameSpan);

        if (this.options.editable) {
            $folder.insert(new Element('button', { type: 'button', className: 'btn btn-secondary', title: idoit.Translate.get('LC__CATEGORY_FOLDERS__EDIT_FOLDER'), 'data-action': 'edit', 'data-tooltip': 1 })
                .update(new Element('img', { src: window.dir_images + 'axialis/basic/tool-pencil.svg' })));
        }

        if (this.options.addable) {
            $folder.insert(new Element('button', { type: 'button', className: 'btn btn-secondary', title: idoit.Translate.get('LC__CATEGORY_FOLDERS__ADD_FOLDER'), 'data-action': 'add', 'data-tooltip': 1 })
                .update(new Element('img', { src: window.dir_images + 'axialis/basic/symbol-add.svg' })));
        }

        if (this.options.movable) {
            $folder.insert(new Element('button', { type: 'button', className: 'btn btn-secondary', title: idoit.Translate.get('LC__CATEGORY_FOLDERS__MOVE_FOLDER'), 'data-action': 'move', 'data-tooltip': 1 })
                .update(new Element('img', { src: window.dir_images + 'axialis/documents-folders/folder-copy-2-grey.svg' })));
        }

        if (this.options.deletable) {
            $folder.insert(new Element('button', { type: 'button', className: 'btn btn-secondary', title: idoit.Translate.get('LC__CATEGORY_FOLDERS__DELETE_FOLDER'), 'data-action': 'delete', 'data-tooltip': 1 })
                .update(new Element('img', { src: window.dir_images + 'axialis/industry-manufacturing/waste-bin.svg' })));
        }

        // Assign the DOM nodes to the data.
        folder.node = $folder;

        $folderContainer.insert($folder);
        $folderContainer.insert(this.subFolderNode(folder));

        return $folderContainer;
    },

    subFolderNode: function (folder) {
        // @see ID-10067 Only hide children, if the folder was not yet opened.
        const $children = new Element('div', {className: 'folder-children ' + (this.openFolders.includes(folder.id) ? '' : 'hide')});
        const $childContainer = new Element('div', { className: 'folder-children-container' });

        $children.update($childContainer)

        // Assign the DOM nodes to the data.
        folder.childContainer = $childContainer;

        return $children;
    },

    categoryNode: function (category) {
        const $category = new Element('div', { className: 'category', 'data-id': category.rawId || 'raw-' + category.type + '-' + category.id });

        $category.insert(new Element('img', { src: window.dir_images + '/axialis/documents-folders/document-color-grey.svg', className: 'ml10 mr5', alt: '' }));

        const $categoryNameSpan = new Element('span');
        // @see ID-10094 Use 'innerText' in order to write TEXT and not risk XSS.
        $categoryNameSpan.innerText = category.name;
        $category.insert($categoryNameSpan);

        if (this.options.movable) {
            $category.insert(new Element('button', { type: 'button', className: 'btn btn-secondary', title: idoit.Translate.get('LC__CATEGORY_FOLDERS__MOVE_CATEGORY'), 'data-action': 'move', 'data-tooltip': 1 })
                .update(new Element('img', { src: window.dir_images + 'axialis/documents-folders/folder-copy-2-grey.svg' })));
        }

        category.node = $category;

        return $category;
    }
});
