(function () {
    this.treeDiagram = function (selector, data, options) {
        let defaults = {
            itemsWidth: null,
            fullScreenContainer: false,
            lineColor: '#333333',
            customItemStyle: false,
            toggleGroups: {
                show: false,
                customClass: 'toggle-group-button',
                customStyle: false
            },
            draggable: {
                show: false,
                customClass: 'reset-draggable',
                customStyle: false,
                resetButtonText: 'Reset drag',
                insertElement: {
                    position: 'beforeend',
                    element: 'body'
                }
            },
            zoom: {
                show: false,
                default: 1,
                customClass: 'buttons-zoom-container',
                customStyle: false,
                zoomInText: 'Zoom in',
                zoomOutText: 'Zoom out',
                insertElement: {
                    position: 'beforeend',
                    element: 'body'
                }
            }
        };

        this.settings = (arguments[2] && typeof arguments[2] === 'object') ? extendDefaults(defaults, arguments[2]) : defaults;
        this.init(selector, data, options);
    };


    /*** Public Methods */

    treeDiagram.prototype.init = function (selector, data) {
        this.container = document.querySelector(selector);
        this.diagram = this.container.firstElementChild;
        this.treeBlock = this.diagram.firstElementChild;
        this.treeItem = this.treeBlock.firstElementChild;
        this.treeBlock.remove();
        this.data = data;
        const createDone = create.call(this);
        if (createDone) {
            if (this.settings.toggleGroups.show) {
                initToggleButtons.call(this);
            }
            initStyles.call(this);
            insertStyles.call(this);
            if (this.settings.draggable.show) {
                initDraggable.call(this);
            }
            if (this.settings.zoom.show) {
                initZoom.call(this);
            }

        }
    };

    function create() {
        this.diagram.insertAdjacentElement("beforeend", createTreeBlock.call(this, this.data));
        if (this.data.hasOwnProperty('children')) {
            recursiveCreate.call(this, this.data['children'], this.data['id']);
        }
        return true;
    }

    /*** Private Methods */
    function createTreeBlock(obj) {
        const resultHtml = this.treeBlock.cloneNode(true);
        resultHtml.setAttribute('data-id', obj['id']);
        for (let key in obj) {
            if (key !== 'id' && key !== 'children') {
                const el = resultHtml.querySelector(`[data-field="${key}"]`);
                if (el) {
                    if (el.tagName === 'IMG') {
                        el.src = obj[key];
                    } else if (el.tagName === 'A') {
                        if (typeof obj[key] === 'object') {
                            const items = Object.keys(obj[key]);
                            el.innerHTML = obj[key][items[0]];
                            el.href = obj[key][items[1]];
                        } else {
                            el.href = obj[key];
                        }
                    } else {
                        el.innerHTML = obj[key];
                    }
                }
            }
        }
        return resultHtml;
    }

    function recursiveCreate(array, id) {
        const treeGroupEl = document.createElement('div');
        const treeBlockParent = this.diagram.querySelector(`[data-id="${id}"]`);
        const treeItem = treeBlockParent.firstElementChild;

        treeGroupEl.classList = 'tree-diagram-group show';
        treeGroupEl.setAttribute('data-group', id);
        treeItem.classList.add('show-group');
        if (array.length > 0) {
            treeItem.classList.add('has-children');
        }
        if (array.length > 1) {
            treeItem.classList.add('has-children-more-one');
            treeGroupEl.classList.add('multi-line');
        }

        if (this.settings.toggleGroups.show) {
            initToggleButton.call(this, id);
        }

        const parentDiagram = treeBlockParent.appendChild(treeGroupEl);

        array.forEach(item => {
            parentDiagram.insertAdjacentElement("beforeend", createTreeBlock.call(this, item));
            if (item.hasOwnProperty('children')) {
                recursiveCreate.call(this, item['children'], item.id);
            }
        });
    }

    function initToggleButton(id) {
        const treeBlockParent = this.diagram.querySelector(`[data-id="${id}"]`);
        const treeItem = treeBlockParent.firstElementChild;
        const toggleButton = document.createElement('span');
        toggleButton.classList = `${this.settings.toggleGroups.customClass} show`;
        toggleButton.setAttribute('data-toggle', id);
        treeItem.appendChild(toggleButton);
    }

    function initStyles() {
        this.style = document.createElement('style');
        this.head = document.head || document.getElementsByTagName('head')[0];
        const classDiagramContainer = this.container.classList.value;
        const classDiagram = this.diagram.classList.value;
        const classTreeBlock = this.treeBlock.classList.value;
        const classTreeItem = this.treeItem.classList.value;
        let widthTreeItem = null;
        if(this.settings.itemsWidth === null){
            widthTreeItem = 'auto';
        } else {
            widthTreeItem = this.settings.itemsWidth + 'px';
        }
        console.log(widthTreeItem);
        this.style.textContent = `
            .${classDiagramContainer}{
                overflow: hidden;
            }

            .${classDiagram} {
                position: relative;
                text-align: center;
                transition: background-color 0.2s ease;
            }

            .${classDiagram}.draggable{
                background-color: #f2f2f2;
            }

            .${classDiagram} > .${classTreeBlock} > .${classTreeItem}::after{
                content: none;
            }

            .${classTreeItem} {
                position: relative;
                display: inline-block;
                width: ${widthTreeItem};
            }

            .${classTreeItem}::after {
                content: '';
                position: absolute;
                top: -21px;
                left: 50%;
                transform: translateX(-50%);
                height: 20px;
                width: 1px;
                background-color: ${this.settings.lineColor};
            }

           .${classTreeItem}.has-children::before {
                content: '';
                position: absolute;
                bottom: -41px;
                height: 40px;
                left: 50%;
                transform: translateX(-50%);
                width: 1px;
                background-color: ${this.settings.lineColor};
            }

            .${classTreeItem}.show-group::before{
                bottom: -61px;
                height: 60px;
            }

            .${classTreeBlock} {
                display: inline-block;
                margin-left: 20px;
            }

            .${classTreeBlock}:first-child {
                margin-left: 0;
            }

            .tree-diagram-group {
                display: flex;
                align-items: flex-start;
                margin-top: 60px;
                padding-top: 20px;
                position: relative;
                visibility: hidden;
                opacity: 0;
                transition: 0.3s all ease;
            }

            .tree-diagram-group.show{
                visibility: visible;
                opacity: 1;
            }

            .tree-diagram-group.multi-line::before {
                content: '';
                position: absolute;
                top: 0;
                height: 1px;
                background-color: ${this.settings.lineColor};
            }
    `;

        if (!this.settings.customItemStyle) {
            const itemStyle = `
             .${classTreeItem} {
                padding: 20px;
                border-radius: 6px;
                border: 1px solid #ddd;
                background-color: #ddd;
             }`;
            this.style.insertAdjacentText('beforeend', itemStyle);
        }

        if (!this.settings.toggleGroups.customStyle) {
            const toggleGroupButton = `
            .${classTreeItem} .${this.settings.toggleGroups.customClass}{
                position: absolute;
                bottom: -45px;
                left: 50%;
                transform: translateX(-50%);
                width: 30px;
                height: 30px;
                border-radius: 50%;
                background-color: #333;
                cursor: pointer;
            }

            .${classTreeItem} .${this.settings.toggleGroups.customClass}::before{
                content: '+';
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                font-size: 25px;
                line-height: 1;
                color: #fff;
            }
            .${classTreeItem} .${this.settings.toggleGroups.customClass}.show::before{
                content: '-';
                top: calc(50% - 2px);
            }`;
            this.style.insertAdjacentText('beforeend', toggleGroupButton);
        }
        this.head.appendChild(this.style);
    }

    function insertStyles() {
        const elementsOfGroup = this.diagram.querySelectorAll('[data-group]');
        if (elementsOfGroup) {
            elementsOfGroup.forEach(item => {
                const childItem = item.querySelectorAll(`.${this.treeBlock.classList.value}`);
                const dataGroup = item.getAttribute('data-group');
                let widthLeft = this.settings.itemsWidth / 2;
                let widthRight = this.settings.itemsWidth / 2;
                if (childItem.length > 0) {
                    widthLeft = (childItem[0].offsetWidth / 2);
                    widthRight = childItem[childItem.length - 1].offsetWidth / 2;
                }
                const treeGroupBefore = `
        .tree-diagram-group[data-group="${dataGroup}"].multi-line::before{
        left: ${widthLeft}px;
        right: ${widthRight}px;
        }`;
                this.style.insertAdjacentText('beforeend', treeGroupBefore);
                this.head.appendChild(this.style);
            });
        }
    }

    function initToggleButtons() {
        const toggleButtons = this.diagram.querySelectorAll('[data-toggle]');

        toggleButtons.forEach(button => {
            const id = button.getAttribute('data-toggle');
            const group = this.diagram.querySelector(`[data-group='${id}']`);
            button.addEventListener('click', function (event) {
                group.classList.toggle('show');
                button.parentElement.classList.toggle('show-group');
                button.classList.toggle('show');
            });
        });
    }

    function initZoom() {
        const diagram = this.diagram;
        const buttonsZoomContainer = document.createElement('div');
        const buttonZoomIn = document.createElement('button');
        const buttonZoomOut = document.createElement('button');
        buttonsZoomContainer.classList = `${this.settings.zoom.customClass}`;
        buttonZoomIn.classList = 'zoom-in';
        buttonZoomOut.classList = 'zoom-out';
        buttonZoomIn.textContent = this.settings.zoom.zoomInText;
        buttonZoomOut.textContent = this.settings.zoom.zoomOutText;
        buttonsZoomContainer.insertAdjacentElement('beforeend', buttonZoomIn);
        buttonsZoomContainer.insertAdjacentElement('beforeend', buttonZoomOut);
        document.querySelector(this.settings.zoom.insertElement.element).insertAdjacentElement(this.settings.zoom.insertElement.position, buttonsZoomContainer);
        diagram.style.zoom = this.settings.zoom.default;
        document.body.querySelector(`.${this.settings.zoom.customClass} .zoom-in`).addEventListener('click', function () {
            diagram.style.zoom = +diagram.style.zoom + 0.12;
        });
        document.body.querySelector(`.${this.settings.zoom.customClass} .zoom-out`).addEventListener('click', function () {
            diagram.style.zoom = +diagram.style.zoom - 0.12;
        });

        if (!this.settings.zoom.customStyle && this.settings.fullScreenContainer) {
            const style = `
                .${this.settings.zoom.customClass}{
                    position: fixed;
                    bottom: 20px;
                    right: 20px;
                    z-index: 1001;
                }`;

            this.style.insertAdjacentText('beforeend', style);
        }
    }

    function initDraggable() {
        const diagramContainer = this.container;
        const diagram = this.diagram;
        const resetButtonBlock = document.createElement('div');
        const resetButton = document.createElement('button');
        resetButtonBlock.classList = this.settings.draggable.customClass;
        resetButton.textContent = this.settings.draggable.resetButtonText;
        resetButtonBlock.insertAdjacentElement('beforeend', resetButton);
        document.querySelector(this.settings.draggable.insertElement.element).insertAdjacentElement(this.settings.draggable.insertElement.position, resetButtonBlock);
        diagram.style.width = diagram.scrollWidth + 'px';
        if (this.settings.fullScreenContainer) {
            diagramContainer.style.width = window.innerWidth + 'px';
            diagramContainer.style.height = window.innerHeight + 'px';
            diagramContainer.style.overflow = 'hidden';
            window.addEventListener('resize', function () {
                diagramContainer.style.width = window.innerWidth + 'px';
                diagramContainer.style.height = window.innerHeight + 'px';
            });
        }

        let isDrag = false;
        let xDrag = 0;
        let yDrag = 0;

        function startDrag(event) {
            isDrag = true;
            xDrag = event.clientX - diagram.offsetLeft;
            yDrag = event.clientY - diagram.offsetTop;
        }

        function stop_drag() {
            isDrag = false;
            diagram.classList.remove('draggable');
        }

        function while_drag(event) {
            diagramContainer.addEventListener('mouseleave', () => {
                return stop_drag();
            });
            if (isDrag) {
                event.cancelBubble = true;
                event.returnValue = false;
                diagram.style.left = (event.clientX - xDrag) + 'px';
                diagram.style.top = (event.clientY - yDrag) + 'px';
                diagram.classList.add('draggable');
            }
        }

        document.body.querySelector(`.${this.settings.draggable.customClass} button`).addEventListener('click', function () {
            diagram.style.left = 0;
            diagram.style.top = 0;
        });

        diagramContainer.addEventListener('mousedown', startDrag);
        diagramContainer.addEventListener('mousemove', while_drag);
        diagramContainer.addEventListener('mouseup', stop_drag);

        if (!this.settings.draggable.customStyle && this.settings.fullScreenContainer) {
            const style = `
                .${this.settings.draggable.customClass}{
                    position: fixed;
                    bottom: 50px;
                    right: 20px;
                    z-index: 1001;
                }`;

            this.style.insertAdjacentText('beforeend', style);
        }
    }

    function extendDefaults(defaults, properties) {
        Object.keys(properties).forEach(property => {
            if (properties.hasOwnProperty(property)) {
                defaults[property] = properties[property];
            }
        });
        return defaults;
    };

}());
