import { eqi18n, Query, DataModel, Column, Expression, DataType, SortDirection, ExprTag } from '@easyquery/core'

export function registerColumnsBarWidget($) {

    var swapArrayItems = function (arr, x, y) {
        if (x === y) return;

        var tmp = arr[x];
        arr[x] = arr[y];
        arr[y] = tmp;
      
    };

    $.widget('eqjs.ColumnsBarOld', {
        _query: null,
        _activeColumn: null,
        _activeColumnIndex: null,
        _contentBlockClass: 'eqjs-cb-inner-wrapper',
        _colsBlockClass: 'eqjs-cb-inner',
        _width: 0, //of ^
        
        _leftBtn: null,
        _rightBtn: null,
        
        _uiUpdaterId: null,

        options: {
            showAddButton: true,
            addButtonSide: 'left',
            addButtonHTML: '+',
            innerPaddingCompensation: {
                right: 38,
                left: 35
            },
            allowAggrColumns: true,
            allowSorting: true,
            sortByList: [
                'None',
                'Ascending',
                'Descending'
            ],
            placeholderHeight: 22,
            adjustEntitiesMenuHeight: true,
            attrElementFormat: '{entity} {attr}',
            alwaysShowButtons: false,
            accentActiveColumn: true,
            uiUpdateInterval: 200,
            scrollSpeed: 15,
            showScrollBar: true,
            scrollButtonsScrollBy: 500,
            scrollButtonsLeftHTML: '&lt; ...',
            scrollButtonsRightHTML: '... &gt;'
        },

        _setOption: function (key, value) {
            if (key == 'query') {
                this.setQuery(value);
            }
            else if (arguments.length == 2) {
                this.options[key] = value;
                this._render();
                return this;
            }
            else {
                return this.options[key];
            }
        },

        _create: function () {this._render()},
        _clear: function () {this.element.html('')},

        getModel: function(): DataModel {
            let query: Query = this.getQuery();
        	return query ? query.getModel() : null;
        },

        getQuery: function(): Query {
            return this._query;
        },

        setQuery: function (query: Query) {
            this._query = query;
            this.refresh();
        },

        init: function (query) {this.setQuery(query)},
        refresh: function () {this._render()},
        resize: function () {this._render()},

        _render: function () {
            this._clear();

            if (!this.getModel()) return;

            this.options.entitiesList = this.getModel().getEntitiesTree({addUIC:false, addUIR:true, addUIS:false});
            this.options.entitiesMenu = this._createEntitiesMenu();

            this._refreshByQuery();
            
            
            var prefix = 'eqjs-cb-with-add-button-on-';
            
            this.element.removeClass(prefix + 'left').removeClass(prefix + 'right');
            if (this.options.showAddButton) {
                this.element.addClass(prefix + this.options.addButtonSide);
                this._showAddButton();
            }
        },

        _refreshByQuery: function () {
            if (!this.getQuery()) return;

            var self = this;

            var colsBlock = $('<div></div>').addClass(
                    self._colsBlockClass
                ),
                contentBlock = $('<div></div>').addClass(
                    self._contentBlockClass
                );

            var query = self.getQuery();    
            var columns = query ? query.getColumns() : null,
                addWidget = function (self, element, idx) {
                    var col = columns[idx];
                    
                    var wfn = element['ColumnsBarColumn_' + col.expr.tag];
                    wfn.call(element, {
                        columnsBar: self,
                        query: self.getQuery()
                    });
                    wfn.call(element, 'init', col);
                    return {wfn: wfn, element: element};
                };
            
            var activateByIdx = (typeof self._activeColumnIndex) === 'number';
            
            for (let colIdx of columns) {
                var d = addWidget(
                    self, $('<div></div>').appendTo(colsBlock), colIdx
                );
                if (activateByIdx && +colIdx === self._activeColumnIndex) {
                    d.wfn.call(d.element, 'setActiveColumn');
                    d.element.click();
                }
            }

            var colsDivs = colsBlock.children('div');

            //inner -> inner-wrapper
            colsBlock.appendTo(contentBlock);

            //inner-wrapper -> self.element
            contentBlock.appendTo(self.element);

            if (!self.options.showScrollBar) {
                contentBlock.css('overflow-x', 'hidden');
                
                self._leftBtn = self._makeScrollButton({
                    html: self.options.scrollButtonsLeftHTML,
                    t: 'left',
                    otherT: 'right',
                    scrollLeft: '-=' + self.options.scrollButtonsScrollBy
                });
                self._rightBtn = self._makeScrollButton({
                    html: self.options.scrollButtonsRightHTML,
                    t: 'right',
                    otherT: 'left',
                    scrollLeft: '+=' + self.options.scrollButtonsScrollBy
                });
                
                self.element.prepend(self._leftBtn.hide());
                self.element.prepend(self._rightBtn.hide());
            } else {
                contentBlock.css('overflow-x', 'auto');
                self._leftBtn = null;
                self._rightBtn = null;
            }

            //hack to update width of cols block ($div.*width() sometimes is 0)
            if (self._uiUpdaterId) {
                clearInterval(self._uiUpdaterId);
            }
            
            self._uiUpdaterId = window.setInterval(function () {
                var initial = 2;
                var total = initial;
                colsDivs.each(function (this) {
                    total += $(this).outerWidth(true);
                });
                if (total > initial && total !== self._width) {
                    self._width = total;
                    self.element.find('.' + self._colsBlockClass).css({
                        width: total
                    });
                }
                if (self._leftBtn && self._rightBtn) {
                    //depend on total width
                    self._updateScrollButtonVisibility(self._leftBtn, 'left');
                    self._updateScrollButtonVisibility(self._rightBtn, 'right');
                }
            }, self.options.uiUpdateInterval);


            var oldIdx, newIdx, addedColumn = false;

            colsBlock.sortable({
                cursor: 'move',
                //containment: 'parent' breaks this
                tolerance: 'pointer',
                cancel: '.eqjs-cb-column-buttonsBlock, .eqjs-cb-colelement, .eqjs-cb-sortbutton, .eqjs-ce-editbox',
                placeholder: 'eqjs-cb-column-placeholder',
                forcePlaceholderSize: true,
                scroll: true,
                scrollSpeed: this.options.scrollSpeed,
                delay: 100,
                distance: 13,
                forceHelperSize: true,
                start: function (event, ui) {
                    oldIdx = ui.item.index();
                    ui.item.addClass('eqjs-cb-column-sort');
                    ui.placeholder.height(self.options.placeholderHeight);
                },
                update: function (event, ui) {
                    newIdx = ui.item.index();

                    ///!!!!!!!!!!!!!!!!!!!!!!! change to Query class method calls
                    if (addedColumn) {
                        addedColumn = false;
                        columns.splice(newIdx, 0, columns.pop());
                        self.refresh();
                    } else {
                        swapArrayItems(columns, oldIdx, newIdx);
                    }
                },
                stop: function (event, ui) {
                    ui.item.removeClass('eqjs-cb-column-sort');
                    ui.item.click();  //activate column
                    self._fireQueryChange('change');
                }
            });

            self.element.droppable({
                tolerance: 'touch',
                hoverClass: 'eqjs-drophover',
                scope: 'entityAttr',
                drop: function (event, ui) {
                    self.addNewColumn(ui.draggable.attr('data-id'));
                    addedColumn = true;
                },
                over: function (event, ui) {
                    ui.helper.addClass('eqjs-cb-column-drag');
                },
                out: function (event, ui) {
                    ui.helper.removeClass('eqjs-cb-column-drag');
                }
            });

            (!colsBlock.children().length) ? contentBlock.hide() : contentBlock.show();
        },

        _makeScrollButton: function (opts) {
            const self = this,
                t = opts.t,
                otherT = opts.otherT;

            var makeClass = function (t) {return 'eqjs-cb-button-' + t};

            var btn = $('<span></span>').addClass(makeClass(t)).html(opts.html);
            
            btn.click(function (e: Event) {
                $(e.target).parent().find('.' + self._contentBlockClass).animate({
                    scrollLeft: opts.scrollLeft
                });
            });

            return btn;
        },

        _updateScrollButtonVisibility: function (btn, t) {
            var innerWrapper = btn.parent().find(
                    '.' + this._contentBlockClass
                );

            var offset = innerWrapper.scrollLeft(),
                width = innerWrapper.innerWidth(),
                totalWidth = this._width;
            
            if (this.options.showAddButton) {
                totalWidth += this.options.innerPaddingCompensation[
                    this.options.addButtonSide
                ];
            }
            
            if ((width >= totalWidth) || !width || offset === null) return btn.hide();

            var edgeOffset = {
                left: function () {return offset},
                right: function () {return totalWidth - (width + offset)}
            }[t]();

            (edgeOffset <= 0) ? btn.hide() : btn.show();
        },

        _showAddButton: function () {
            var side = this.options.addButtonSide;
            var addColumnBlock = $('<div class="eqjs-cb-add-button"></div>');
                addColumnBlock.prependTo(this.element).css(
                    side,
                    this.element.css('padding-' + side)
                );

            var addColumnLink = $('<a href="javascript:void(0)"></a>').html(
                this.options.addButtonHTML
            ).appendTo(addColumnBlock);

            var self = this;

            addColumnLink.click(function () {
                self.showEntitiesMenu({
                    anchor: addColumnLink,
                    selectedIds: null,
                    itemSelectedCallback: function (evt, data) {
                        var attrid = data.menuItem.id;
                        var attrobj = self.getModel().getAttributeById(attrid);
                        self.addNewColumn(attrid);
                        return false;
                    }
                });
            });
        },

        showEntitiesMenu: function (menuOptions, eventArgs) {
            const self = this;

            self.options.entitiesMenu.PopupMenu('showMenu', menuOptions);
        },

        clearColumns: function (needRefresh) {
            this.getQuery().clearColumns();

            if (needRefresh !== false) this.refresh();
        },

        _createEntitiesMenu: function () {
            if (!this.getModel()) return null;

            var menuDiv = $('<div></div>').hide().appendTo(this.element);

            menuDiv.PopupMenu({
                items: this.options.entitiesList,
                adjustHeight: this.options.adjustEntitiesMenuHeight
            });

            return menuDiv;
        },

        _fireQueryChange: function (changeType) {
            var self = this;
            this.getQuery().fireChangedEvent({
                "author": self,
                "changeType": 'query.' + changeType
            });
        },

        _fireColumnChange: function (changeType, column) {
            const self = this;

            column = column || self._column;

            this.getQuery().fireChangedEvent({
                "author": self,
                "changeType": 'column.' + changeType,
                "column": column
            });
        },

        addNewColumn: function (attrid, index) {
            var self = this;
            var getColumnByAttrId = function (attrid): Column {
                let model: DataModel = self.getModel();
                var attribute = model.getAttributeById(attrid);

                if (!attribute) { return null; }

                if (!attribute.usedInResult) {
                    if (attribute.lookupAttr) {
                        attrid = attribute.lookupAttr;
                        attribute =model.getAttributeById(attrid);
                        if (!attribute || !attribute.usedInResult) {
                            return null;
                        }
                    }
                    else
                        return null;
                }

                let column = new Column();
                column.sorting = SortDirection.None;
                column.sortIndex = -1;

                let expr = new Expression();
                expr.id = attrid;
                expr.tag = ExprTag.EntityAttribute;

                column.expr = expr;

            };

            if (!self.getQuery()) { return; }

            var colToAdd,
                col;

            if ($.isArray(attrid)) {
                colToAdd = [];
                for (var i = 0; i < attrid.length; i++) {
                    col = getColumnByAttrId(attrid[i]);
                    if (col) {
                        colToAdd.push(col);
                    }
                }
            }
            else
                colToAdd = getColumnByAttrId(attrid);

            self.addColumnObj(colToAdd, index);

            return colToAdd;
        },

        addColumn: function (column, index) {
            var self = this;

            let query: Query = this.getQuery();
            if (!query) { return; }
            let columns = query.getColumns();

            if (!column || ($.isArray(column) && column.length == 0)) { return; }

            let columnArr = [].concat(column);
            if (typeof index === 'number') {
                columns.splice(index, 0, ...columnArr);
            }
            else {
                columns.push(...columnArr);
            }

            self._width += (self._initialWidth * 4);
            self.refresh();
            self._fireColumnChange('add', column);
        },

        removeColumn: function (column) {
            const self = this;

            let query: Query = this.getQuery();
            if (!query) { return; }
            let columns = query.getColumns();

            let index = $.inArray(column, columns);
            if (index >= 0) {
                if (index === self._activeColumnIndex) {
                    self._activeColumnIndex = null;
                };
                
                columns.splice(index, 1);
                self.refresh();
                self._fireColumnChange('remove', column);
            }
        },

        removeColumnByAttrId: function (attrId) {
            const self = this;

            let query: Query = this.getQuery();
            if (!query) { return; }
            let columns = query.getColumns();

            let index = 0;
            var colCount = columns.length;
            while (index < colCount) {
                var col = columns[index];
                if (col.expr.tag == ExprTag.EntityAttribute 
                    && col.expr.id == attrId) break;

                index++;
            }

            if (index < colCount) {
                columns.splice(index, 1);
                self._fireColumnChange('remove', col);
                self.refresh();
            }
        },

        setActiveColumn: function (column) {
            if (this._activeColumn) {
                this._activeColumn.deactivate();
            }

            this._activeColumn = column;
            if (this._activeColumn) {
                this._activeColumn.activate();
            }
        },
        
        getActiveColumn: function () {
            return this._activeColumn;
        },
        
        setActiveColumnIndex: function (idx) {
            this._activeColumnIndex = idx;
        }
    });


    var swapColumns = function (ctx, x, y) {
        var cols = ctx.self.options.columnsBar._query.getColumns();
        if (!cols.length) return;
        
        swapArrayItems(cols, x, y);
        
        var activeCol = ctx.self.options.columnsBar.getActiveColumn();
        if (activeCol && activeCol._active && activeCol.element.length) {
            var idx = activeCol.element.index();
            if (idx === x) {
                return y
            } else if (idx === y) {
                return x;
            };
        }
    };

    var refreshByColumnElement = function (activeColIndex, el) {
        var cb = el.parent().parent().parent();
        
        if ((typeof activeColIndex) !== 'undefined') {
            cb.ColumnsBar('setActiveColumnIndex', activeColIndex);
        };
        
        cb.ColumnsBar('refresh');
    };


    var COLUMN_MENU_BUTTON_MENU_COMMON_ITEMS = [
        {
            id: '|<-',
            text: eqi18n.getText('CmdMoveToStart'),
            fn: function (ctx) {
                refreshByColumnElement(
                    swapColumns(ctx, 0, ctx.self.element.index()),
                    ctx.self.element
                );
            }
        },
        {
            id: '->',
            text: eqi18n.getText('CmdMoveRight'),
            fn: function (ctx) {
                var currentEl = ctx.self.element;
                var nextEl = currentEl.next();

                if (!nextEl.length) return;

                refreshByColumnElement(
                    swapColumns(ctx, nextEl.index(), currentEl.index()),
                    currentEl
                );
            }
        },
        {
            id: '<-',
            text: eqi18n.getText('CmdMoveLeft'),
            fn: function (ctx) {
                var currentEl = ctx.self.element;
                var prevEl = currentEl.prev();

                if (!prevEl.length) return;

                refreshByColumnElement(
                    swapColumns(ctx, prevEl.index(), currentEl.index()),
                    currentEl
                );
            }
        },
        {
            id: '->|',
            text: eqi18n.getText('CmdMoveToEnd'),
            fn: function (ctx) {
                var currentEl = ctx.self.element;
                var lastEl = currentEl.parent().children().last();

                if (!lastEl.length) return;

                refreshByColumnElement(
                    swapColumns(ctx, lastEl.index(), currentEl.index()),
                    currentEl
                );
            }
        }
    ];
    
    var rrNext = function (arr, currEl) {
        var nextIdx = arr.indexOf(currEl) + 1;
        return (nextIdx == arr.length) ? arr[0] : arr[nextIdx];
    };

    $.widget('eqjs.ColumnsBarColumn', {
        _column: null,

        _sortingButton: null,

        _buttonsBlock: null,
        _menuButton: null,
        _menuButtonMenu: null,
        _deleteButton: null,

        _keepShowingButtons: false,
        _isMouseOverBlock: false,

        _classesToAdd: '',

        _active: false,

        options: {
            columnsBar: null,
            query: null
        },

        getQuery: function():Query {
            return this.options.columnsBar.getQuery();
        }, 

        getModel: function(): DataModel {
            let query: Query = this.getQuery();
            return query ? query.getModel() : null;
        },

        init: function (column) {
            this._column = column;
            this.refresh();
        },

        _render: function () {
            this._clear();
            if (this.getModel() && this._column) {
                this._refreshByColumn();
                this._initButtons();
            }
        },

        _fireColumnChange: function (changeType, column) {
            const self = this;

            column = column || self._column;
            let query: Query = this.qetQuery();
            if (!query) return;    
            
            query.fireChangedEvent({
                query: self.options.columnsBar.options.query,
                changeType: 'column.' + changeType,
                condition: column
            });
        },

        refresh: function () {this._render()},

        _setOption: function (key, value) {
            if (arguments.length == 2) {
                this.options[key] = value;
                if (key === 'disabled') {
                    this._column.enabled = !value;
                }

                this._render();
                return this;
            }
            else {
                if (key === 'disabled')
                    return this._column.enabled === false;
                else
                    return this.options[key];
            }
        },

        _clear: function () {
            this.element.unbind();
            this.element.html('');
            this.element.removeClass();
        },

        _refreshByColumn: function () {},

        remove: function () {
            const self = this;

            if (!self.options.columnsBar) return;

            self.options.columnsBar.removeColumn(self._column);
            self._fireColumnChange('delete');
        },

        _initMenu: function () {
            var items = this._getMenuButtonMenuItems();

            var callIfFound = function (its, id, ctx) {
                for (var i = 0, length = its.length; i < length; i++) {
                    var current = its[i];
                    if (current.items) {
                        var r = callIfFound(current.items, id, ctx);
                        if (r) {
                            return true;
                        }
                    } else {
                        if (id == current.id) {
                            if (current.fn) {
                                current.fn(ctx);
                            }
                            return true;
                        }
                    }
                }
                return false;
            }

            this._menuButtonMenu = $('<div></div>').hide().appendTo(this.element);
            this._menuButtonMenu.PopupMenu({items: items});

            var self = this;

            self._menuButton.click(function () {
                self._menuButtonMenu.PopupMenu('showMenu', {
                    anchor: self._menuButton,
                    selectedIds: null,
                    itemSelectedCallback: function (evt, data) {
                        callIfFound(items, data.menuItem.id, {
                            self: self,
                            evt: evt,
                            data:data
                        });
                        return false;
                    },
                    menuClosedCallback: function () {
                        if (!self._isMouseOverBlock) {
                            self._menuButtonMenu.trigger('mouseleave');
                        }
                    }
                });
            });
        },

        _initButtons: function () {
            var self = this;

            var container = self.element;

            if (!container) return;

            self._buttonsBlock = $('<span></span>')
                .addClass('eqjs-cb-column-buttonsBlock')
                .appendTo(container);

            self._menuButton = $('<span></span>')
                .addClass('eqjs-cb-column-button-menu')
                .attr('title', eqi18n.getText('ButtonMenu'))
                .appendTo(self._buttonsBlock);
            self._initMenu();

            self._deleteButton = $('<span></span>')
                .addClass('eqjs-cb-column-button-delete')
                .attr('title', eqi18n.getText('ButtonDelete'))
                .appendTo(self._buttonsBlock)
                .click(function () {
                    self.remove();
                });

            //sorting button setup
            self._sortingButton = $('<span></span>')
                .addClass('eqjs-colelement eqjs-cb-button-sort')
                .prependTo(self.element);

            if (self.options.columnsBar.options.allowSorting != false) {
                self._sortingButton
                    .attr('title', eqi18n.getText('ButtonSorting'))
                    .click(function () {
                        self._column.sorting = rrNext(
                            self.options.columnsBar.options.sortByList,
                            self._column.sorting
                        );
                        self.refresh();
                        self._fireColumnChange('change');
                    });

                if (self._column.sorting === SortDirection.None) {
                    self._sortingButton.addClass('eqjs-cb-column-button-sort-none')
                }

                self._sortingButton.hover(
                    function (e: Event) {
                        $(e.target).addClass('eqjs-cb-column-button-sort-active');
                    },
                    function (e: Event) {
                        $(e.target).removeClass('eqjs-cb-column-button-sort-active');
                    }
                );
            }

            if (self._column.sorting === SortDirection.Ascending) {
                self._sortingButton
                    .addClass('eqjs-cb-column-button-sort-asc')
                    .attr('title', eqi18n.getText('ButtonSorting'));
            }
            else if (self._column.sorting === SortDirection.Descending) {
                self._sortingButton
                    .addClass('eqjs-cb-column-button-sort-desc')
                    .attr('title', eqi18n.getText('ButtonSorting'));
            }

            container.find('[class*=eqjs-column-button]').hover(
                function (e: Event) {
                    $(e.target).addClass('eqjs-cb-column-button-sort-active');
                },
                function (e: Event) {
                    $(e.target).removeClass('eqjs-cb-column-button-sort-active');
                }
            )

            container.bind('mouseenter', function (event) {
                self._isMouseOverBlock = true;
                self._enterButtonBlock();
                event.stopPropagation();
                return false;
            })
            .bind('mouseleave', function (event) {
                self._isMouseOverBlock = false;
                if (!self._keepShowingButtons) {
                    self._leaveButtonBlock();
                }

                event.stopPropagation();
                return false;
            })

            self._hideButtons();
            self._adjustButtonsVisibility();
        },

        _adjustButtonsVisibility: function () {
            if (
                this.options.columnsBar.options.alwaysShowButtons ||
                (
                    this.options.columnsBar.options.accentActiveColumn &&
                    this._active
                )
            ) {
                this._showButtons();
            }

        },

        _enterButtonBlock: function () {this._showButtons()},

        _leaveButtonBlock: function () {
            if (this.options.columnsBar.options.alwaysShowButtons != true && (!this._active || this.options.columnsBar.options.accentActiveColumn != true)) {
                this._hideButtons();
            }
        },

        _showButtons: function () {
            var show = function (obj) {obj.css('visibility', 'visible')};

            show(this._deleteButton);
            show(this._menuButton);

            if (this.options.columnsBar.options.allowSorting != false) {
                show(this._sortingButton);
            }
        },

        _hideButtons: function () {
            var hide = function (obj) {obj.css('visibility', 'hidden')};

            hide(this._deleteButton);
            hide(this._menuButton);

            if (this._column.sorting === SortDirection.None) {
                hide(this._sortingButton);
            }

        },

        activate: function () {
            this._active = true;
            this.adjustActiveClass();

            this._hideButtons();
            this._adjustButtonsVisibility();
        },

        deactivate: function () {
            this._active = false;
            this.adjustActiveClass();

            this._hideButtons();
            this._adjustButtonsVisibility();
        },

        adjustActiveClass: function () {
            if (this._active) {
                this.element.addClass('active');
            }
            else {
                this.element.removeClass('active');
            }
        },

        isActive: function () {
            return this._active;
        },

        setActiveColumn: function () {
            this.options.columnsBar.setActiveColumn(this);
        }

    });
    
    var _prependMenuSeparator = function (l) {
        return [{id: 'sep', text: '-------'}].concat(l);
    }
    
    $.widget(`eqjs.ColumnsBarColumn_${ExprTag.EntityAttribute}`, $.eqjs.ColumnsBarColumn, {
        _classesToAdd: 'eqjs-cb-column eqjs-cb-column-entattr',
        _baseAttr: null,
        _menuFunctionsBlock: null,

        _getMenuButtonMenuItems: function () {
            if (!this.options.columnsBar.options.allowAggrColumns) {
                return COLUMN_MENU_BUTTON_MENU_COMMON_ITEMS;
            }

            var fnmis = this._createFunctionMenuItems(function (ctx) {
                    ctx.self._changeTypeToAggr(ctx.data.menuItem.id);
                }),
                cmenu = COLUMN_MENU_BUTTON_MENU_COMMON_ITEMS;
            
            if (fnmis) {
                cmenu = _prependMenuSeparator(cmenu) as any;
            }
            
            return fnmis.concat(cmenu);
        },

        _fillBaseBlock: function (baseBlock) {
            baseBlock.addClass('eqjs-expr-block');

            this._baseAttr = this._getAttribute();
            
            if (!this._column.caption) {
                this._column.caption = this._getDefaultCaption();
            }
        },

        _refreshByColumn: function () {
            if (!this._column || !this.getModel()) return;
            var self = this;

            self.element.addClass(self._classesToAdd);

            var baseBlock = $('<span></span>')
                .appendTo(self.element);

            self._fillBaseBlock(baseBlock);

            if (self.options.columnsBar.options.accentActiveColumn) {
                self.element.click(function () {
                    if (!self._active) {
                        self.setActiveColumn();
                    }
                })
            }
            
            if (!self._column.caption || self._column.caption == '') {
                self._column.caption = self._getDefaultCaption();
            }

            var captionEditorBlock = $('<span></span>');
            captionEditorBlock.appendTo(this.element);

            var captionChanged = function() {
                self._fireColumnChange('change');
            }

            captionEditorBlock.ColumnsBarColumn_CaptionEditor();
            captionEditorBlock.ColumnsBarColumn_CaptionEditor('init', self._column, self._getDefaultCaption(), captionChanged);

            self.adjustActiveClass();
        },

        _getDefaultCaption: function () {
            return this._getAttributeText(this._baseAttr);
        },

        _getAttribute: function () {
            return this.getModel().getAttributeById(this._column.expr.id)
        },

        _getAttributeText: function (attribute) {
            var self = this;

            if (!attribute) return '';

            var attrText = eqi18n.getText('Attributes', attribute.id);
            if (!attrText)
                attrText = attribute.caption;

            if (!self.options.columnsBar) return attrText;

            var format = self.options.columnsBar.options.attrElementFormat;
            if (!format) return attrText;

            var result = format.replace(new RegExp('{attr}','g'), attrText);
            var entityPath = self.getModel().getFullEntityPathByAttr(attribute.id, '.');
            result = result.replace(new RegExp('{entity}','g'), entityPath);

            return result;
        },

        _changeTypeToAggr: function (funcId) {
            var self = this;

            if (!funcId)
                funcId = self._menuFunctionsBlock.PopupMenu('option', 'items')[0].id;

            let id = self._column.expr.id;

            let expr = new Expression();
            expr.tag = ExprTag.AggregateFunction;
            expr.distinct = false;
            expr.func = funcId;

            let arg = new Expression();
            arg.tag = ExprTag.EntityAttribute;
            arg.id = id;
            expr.args.push(arg);


            self._column.expr = expr;

            var column = self._column;
            var isActive = self._active;
            self.destroy();

            self._clear();
            self.element.ColumnsBarColumn_AGGRFUNC({columnsBar: self.options.columnsBar, model: self.getModel()});
            self.element.ColumnsBarColumn_AGGRFUNC('init', column);
            self._fireColumnChange('change');

            if (isActive) {
                self.options.columnsBar._activeColumn = null;
                self.element.ColumnsBarColumn_AGGRFUNC('setActiveColumn');
            }
        },

        _createFunctionMenuItems: function (fn) {
            var self = this;
            let model: DataModel = self.getModel();
            var items = [];
            var list = model.aggrFunctions; // EQ.core.AggrFunctions;
            var toInclude = true;
            var funcAllowed = true;

            var item = null;

            var attribute = self._getAttribute();
            var funcCaption;

            for (var idx in list) {
                if (!list[idx]) continue;

                toInclude = true;

                if (list[idx].id === 'SUM' || list[idx].id === 'AVG') {
                    toInclude = $.inArray(attribute.dataType, [DataType.Autoinc, DataType.Byte, DataType.Currency, DataType.Float, 
                                                              DataType.Int32, DataType.Int64, DataType.Word]) >= 0;
                }
                else if (list[idx].id === 'MIN' || list[idx].id === 'MAX') {
                    toInclude = $.inArray(attribute.dataType, [DataType.Autoinc, DataType.BCD, DataType.Byte, DataType.Currency, DataType.Date, 
                                                              DataType.DateTime, DataType.Float, DataType.Time, DataType.Int32, DataType.Int64, DataType.Word]) >= 0;
                }

                if (self._column.expr.func === list[idx].id && !toInclude) {
                    funcAllowed = false;
                }

                if (toInclude) {
                    funcCaption = model.getAggrFunctionCaption(list[idx].id);

                    item = {
                        id: list[idx].id,
                        text: funcCaption,
                        fn: fn
                    };
                    
                    if (self._column.expr.func !== item.id) items.push(item);
                }
            }

            if (items && !funcAllowed) {
                self._column.expr.func = items[0].id;
            }

            return items;
        }
    });

    $.widget(`eqjs.ColumnsBarColumn_${ExprTag.AggregateFunction}`, $.eqjs[`ColumnsBarColumn_${ExprTag.EntityAttribute}`], {

        _classesToAdd: 'eqjs-cb-column eqjs-cb-column-aggr',

        _getMenuButtonMenuItems: function () {
            return [
                {
                    id: 'to-simple',
                    text: eqi18n.getText('CmdToSimple'),
                    fn: function (ctx) {
                        ctx.self._changeTypeToSimple();
                    }
                }
            ].concat(this._createFunctionMenuItems(function (ctx) {
                ctx.self._changeTypeToSimple();
                ctx.self._changeTypeToAggr(ctx.data.menuItem.id);
                
                ctx.self.refresh();
                ctx.self._fireColumnChange('change');
            })).concat(
                _prependMenuSeparator(COLUMN_MENU_BUTTON_MENU_COMMON_ITEMS) as any
            );
        },

        _displayFormatParser: {

            formatStr: '',
            pos: 0,
            token: 'text',
            tokenText: '',

            start: function (s) {
                this.formatStr = s;
                this.pos = 0;
                this.tokenText = '';
            },

            skipSpaces: function () {
                while (this.pos < this.formatStr.length && this.formatStr.charAt(this.pos) === ' ') this.pos++;
            },

            next: function () {
                this.skipSpaces();
                if (this.pos >= this.formatStr.length) return false;

                var npos = 0;
                if (this.formatStr.charAt(this.pos) === '{') {
                    npos = this.formatStr.indexOf('}', this.pos);
                    if (npos < 0) return false;
                    this.tokenText = this.formatStr.substring(this.pos, npos+1);
                    if (this.tokenText.indexOf('{attr') === 0) {
                        this.token = 'attribute';
                    }
                    this.pos = npos + 1;
                }
                else if (this.formatStr.charAt(this.pos) === '[' && this.pos < this.formatStr.length-1 && this.formatStr.charAt(this.pos+1) === '[') {
                    this.pos += 2;
                    npos = this.formatStr.indexOf(']]', this.pos);
                    this.token = 'function';
                    this.tokenText = this.formatStr.substring(this.pos, npos);
                    this.pos = npos + 2;
                }
                else {
                    this.token = 'text';
                    var npos1 = this.formatStr.indexOf('{', this.pos);
                    if (npos1 < 0) npos1 = this.formatStr.length;
                    var npos2 = this.formatStr.indexOf('[[', this.pos);
                    if (npos2 < 0) npos2 = this.formatStr.length;
                    npos = Math.min(npos1, npos2);
                    this.tokenText = $.trim(this.formatStr.substring(this.pos, npos));
                    this.pos = npos;
                }
                return true;
            }

        },

        _parseDisplayFormat: function (format) {
            if (!format) return [];

            var result = [];

            var parser = this._displayFormatParser;
            parser.start(format);

            while (parser.next()) {
                if (parser.token === 'function') {
                    result.push({type: 'function', text: parser.tokenText});
                }
                else if (parser.token === 'attribute') {
                    result.push({type: 'attribute'});
                }
                else if (parser.token === 'text') {
                    result.push({type: 'text', text: parser.tokenText});
                }
            }

            return result;
        },

        _fillBaseBlock: function (baseBlock) {
            var self = this;

            baseBlock.addClass('eqjs-expr-block eqjs-expr-block-aggr');

            var format = self.getModel().getAggrFunctionFormat(self._column.expr.func);
            if (!format || format === '') return;

            var arrFormat = self._parseDisplayFormat(format);
            if (arrFormat.length === 0) return;
            
            var chunks = [];
            
            var item, type;
            for (var i = 0, length = arrFormat.length; i < length; i++) {
                item = arrFormat[i];
                type = item.type;
                
                if (type === 'function' || type === 'text') {
                    chunks.push(item.text);
                }
                else if (type === 'attribute') {
                    self._baseAttr = self._getAttribute();
                    chunks.push(self._getAttributeText(self._baseAttr));
                }
            }
            
            this._column.caption = chunks.join(' ');
        },

        _getDefaultCaption: function () {
            return this._getAttributeText(this._baseAttr) + ' ' + this.getModel().getAggrFunctionCaption(this._column.expr.func);
        },

        _getAttribute: function () {
            return this.getModel().getAttributeById(this._column.expr.argument.id)
        },

        _changeTypeToSimple: function () {
            var self = this;

            var id = self._column.expr.argument.id;
            self._column.caption = '';

            var expr = new Expression();
            expr.id = id;
            expr.tag = ExprTag.EntityAttribute;

            self._column.expr = expr;
           

            var column = self._column;
            var isActive = self._active;
            self.destroy();

            self._clear();
            self.element.ColumnsBarColumn_ENTATTR({columnsBar: self.options.columnsBar, model: self.getModel()});
            self.element.ColumnsBarColumn_ENTATTR('init', column);
            self._fireColumnChange('change');

            if (isActive) {
                self.options.columnsBar._activeColumn = null;
                self.element.ColumnsBarColumn_ENTATTR('setActiveColumn');
            }
        }

    });

    $.widget('eqjs.ColumnsBarColumn_CaptionEditor', $.eqjs.ValueEditor_EDIT, {
        _column : null,
        _defaultValue: '',
        _editBoxClass: 'eqjs-ce-editbox',
        _onChange : null,

        init: function (column, defaultValue, changeCallback) {
            this._expr.value = column.caption;
            this._expr.text = column.caption;
            this._column = column;
            this._defaultValue = defaultValue;
            this._onChange = changeCallback;
            this.refresh();
        },

        _render: function () {
            this.clear();
            if (this._column) {
                this._renderCommonPart();
                this._renderEditor();
                this._linkElement.text(this._getDisplayText());
            }
        },

        _renderCommonPart: function () {
            var self = this;

            self.element.addClass(self._getClassesToAdd());

            self._linkElement = $('<span></span>', {text: '-'}).appendTo(
                self.element
            );

            self._linkElement.dblclick(function () {
                self._showEditor();
                return false;
            });
        },

        _getClassesToAdd: function () {
            return 'eqjs-colelement eqjs-captionelement';
        },

        _setValue: function (newValue) {
            if (!newValue || newValue == '')
                this._column.caption = this._defaultValue;
            else
                this._column.caption = newValue;

            this._linkElement.text(this._column.caption);

            if (this._onChange) {
                this._onChange(newValue);
            }

            return true;
        },

        _getValue: function () {
            return this._column.caption;
        },

        _getText: function () {
            return this._column.caption;
        },

        _getDisplayText: function () {
            return this._column.caption;
        },

        _getZIndex: function () {
            return 100000;
        }
    });

}

