/*global UB, js_beautify, mxEvent, mxUtils, mxEditor, mxRubberband, mxCodec, mxBasePath, mxVertexHandler, mxCellRenderer, mxCell, mxGeometry, mxPoint, console, isDeveloperMode, mxRubberband */
//Ext.Loader.loadScript({
//    url: mxBasePath + "/js/graphEditor/Shapes.js"
//});
/*
if (!UB.isJSLINTLoaded) {
    Ext.Loader.loadScript('models/UBS/js_beautify.js');
    Ext.Loader.loadScript('models/UBS/jslint.js');
    UB.isJSLINTLoaded = true;
}
*/

/**
 * Metadata ER diagram editor.
 */
Ext.define('UB.ux.UBMetaDiagram', {
    extend: 'Ext.Panel',
    alias: 'widget.UBMetaDiagram',
    width: '100%',
    height: '100%',
    layout: 'fit',
    //cls: 'mx-graph-editor',
    statics: {
        mixinPictures: {
            mStorage: '<i class="fa fa-database"></i>',
            dataHistory: '<i class="fa fa-clock-o"></i>',
            unity: '<i class="fa fa-toggle-off"></i>',
            tree: '<i class="fa fa-sitemap"></i>',
            fts: '<i class="fa fa-search"></i>',
            rls: '<i class="fa fa-tasks"></i>',
            als: '<i class="fa fa-table"></i>',
            aclRls: '<i class="fa fa-tasks">a</i>',
            audit: '<i class="fa fa-pencil-square-o"></i>',
            softLock: '<i class="fa fa-key"></i>',
            clobTruncate: 'ct'
        }
    },
    initComponent: function(){
        var me = this, id, html, toolbarHtml, outlineHtml;

        UB.inject("models/UBS/js_beautify.js").done();
        //UB.inject("models/UBS/jslint.js").done();

        me.addEvents('change');
        me.items = me.items || [];
        me.initObjectTree();

        Ext.EventManager.on(document, 'mouseup', me.onmouseUpFunc ,me);
        Ext.EventManager.on(document, 'mousemove', me.onmousemoveFunc ,me);

        me.idPrefix  = Ext.id(null);
        me.containerId = id =  me.idPrefix + 'graph';
        html = '<div id="' + id+'" class="graph-editor-holder" style="width: 100%; height: 100%;-moz-user-select: none; -webkit-user-select: none; -ms-user-select:none; user-select:none;" tabindex="0"></div>';

        me.editPnl =  Ext.create('Ext.panel.Panel',{
            //layout: 'fit',
            region:'center',
            cls: 'mx-graph-editor',
            flex: 1,
            html: html,
            listeners: {
                boxready: function(/*sender*/){
                    if (isDeveloperMode){
                        me.initMXEditor();
                    } else {
                        UB.inject('jslibs/mxGraph/javascript/src/js/mxClient.js')
                            .then(function(){
                                return UB.inject(mxBasePath + '/js/graphEditor/Shapes.js');
                            }).done(function(){
                                me.initMXEditor();
                            });
                    }

                },
                resize: function(){
                    if (this.editor && this.editor.graph){
                        this.editor.graph.sizeDidChange();
                    }
                },
                scope: me
            }
        });

        me.toolbarId = id =  me.idPrefix + 'toolbar';
        toolbarHtml = '<div id="' + id+'" tabindex="0"></div>';
        me.outlineId = id =  me.idPrefix + 'outline';
        outlineHtml = '<div id="' + id+'" ></div>';
        me.menuPnl =  Ext.create('Ext.panel.Panel',{
            height: '100%',
            region:'west',
            split: true,
            collapsible: true,
            width: 250,
            layout: {
                type: 'border'
            },
            items: [
                {
                    xtype: 'tabpanel',
                    region:'center',
                    deferredRender: false,
                    split: true,
                    height: 'auto',
                    bodyStyle: 'background:white;',
                    items : [
                            me.objectTree,
                            {
                                xtype: 'panel',
                                region:'center',
                                autoRender: true,
                                title: UB.i18n('toolbar'),
                                html: toolbarHtml,
                                listeners: {
                                    boxready: function(sender){
                                        //Ext.get(me.toolbarId).dom.appendChild(me.editor.toolbar.toolbar.container);
                                    },
                                    scope: me
                                }

                            }
                    ]
                },
                {
                    xtype: 'panel',
                    region:'south',
                    split: true,
                    height: 160,
                    fit: 2,
                    //layout: 'fit',
                    html: outlineHtml
                }
            ]
        });


        var innerPnl =  Ext.create('Ext.panel.Panel',{
            layout: {
                type: 'border'
            },
            defaults: {
                split: true
            },
            items: [
                me.menuPnl,
                me.editPnl
            ]
//            ,listeners: {
//                boxready: function(sender){
//                    this.initMainPanel(sender);
//                },
//                scope: me
//            }
        });
        me.items.push(innerPnl);

        me.callParent(arguments);

    },


    isDirty: function(){
       return (this.editor ? this.editor.isModified(): false);
    },

//    initMainPanel: function(sender){
//        /*
//        var me = this, basepanel, saveAction;
//        basepanel = me.up('basepanel');
//        me.docContainer = basepanel.getUBCmp('attrDocument');
//
//
//        saveAction =  basepanel.actions[UB.view.BasePanel.actionId.save];
//        me.baseSaveActionHandler = Ext.bind(saveAction.initialConfig.handler, basepanel);
//        saveAction.setHandler(me.onBaseFormSave, me);
//       */
//    },


    onmousemoveFunc:  function(evnt){
        var me = this;
        if (!me.dragDropObj || !me.dragDropObj.dragElm){
            return;
        }
        var currXY= evnt.getXY();
        //basewindow.dom.style.zIndex + 2,
        Ext.get(me.dragDropObj.dragElm).position("absolute", me.dragDropObj.dragElm.style.zIndex, currXY[0] , currXY[1]  );
    },


    onmouseUpFunc: function(e){
        var me = this, x, y, pt;
        if (!me.dragDropObj){
            return;
        }
        if (!me.graphDivEl){
            return;
        }

        if (me.dragDropObj.dragElm){

            me.dragDropObj.dragElm.parentNode.removeChild(me.dragDropObj.dragElm);
        }
        x = e.getX();
        y = e.getY();
        if (me.graphDivEl.getX() > x ||
            me.graphDivEl.getX() + me.graphDivEl.getWidth() < x ||
            me.graphDivEl.getY() > y ||
            me.graphDivEl.getY() + me.graphDivEl.getHeight() < y ){
            me.dragDropObj = null;
            return;
        }
        x = mxEvent.getClientX(e.browserEvent);
        y = mxEvent.getClientY(e.browserEvent);
        pt = mxUtils.convertPoint( me.editor.graph.container, x, y);

        me.addObjectToDiagram(pt, me.dragDropObj);
        me.dragDropObj = null;
    },

    addObjectToDiagram: function(pt, config){
        var me = this, vertex, doc, node, metaObj, objLinks = {}, linkmetaObj, lnkType,
            objCode = config.objectName, objExists = false, lnk;


        // find exist UB obj
        Ext.Object.each(me.editor.graph.model.cells, function(id,cell){
            var objectCode = cell.getAttribute('objectCode');
            if (!objectCode){
                return true;
            }
            if (objCode.toUpperCase() === objectCode.toUpperCase()){
                objExists = true;
                return false;
            }
        },me);

        if (objExists){
            $App.dialogError('Object exist in diagram').done();
            return false;
        }
        doc = mxUtils.createXmlDocument();
        node = doc.createElement('ubObject');
        node.setAttribute('label', objCode );
        node.setAttribute('objectCode', objCode);
               //;verticalAlign=top;align=left;spacingLeft=36; overflow=fill; shape=component; spacingTop=2;spacingLeft=2;spacingRight=2;spacingBottom=2;
        vertex = new mxCell(node, new mxGeometry(0, 0, 187, 147), 'verticalAlign=top;align=left;overflow=fill;html=1');
        vertex.vertex = true;
        //vertex.setId(me.dragDropObj.objectName);

        metaObj = $App.domainInfo.get(objCode);

        me.editor.addVertex( null, vertex, pt.x, pt.y); // me.editor.graph.getDefaultParent()


        metaObj.eachAttribute(function(attr, attrCode){
           if (Ext.String.startsWith(attrCode, 'mi_', true )){
               return true;
           }
           if (attr.dataType === 'Entity' && attrCode.toUpperCase() !== 'ID' ){
               lnk = objLinks[attr.associatedEntity];
               if (!lnk){
                   objLinks[attr.associatedEntity] = lnk = [];
               }
               lnk.push({ linkType: 'relation', srcObj: objCode,
                   srcProp: attrCode , destObj: attr.associatedEntity, destProp: 'ID' });
           }
        }, me);

        if  (metaObj.mixins && metaObj.mixins.unity /*&& metaObj.mixins.unity.enabled */){
            lnk = objLinks[metaObj.mixins.unity.entity];
            if (!lnk){
                objLinks[metaObj.mixins.unity.entity] = lnk = [];
            }
            lnk.push({ linkType: 'inheritage', srcObj: objCode,
                srcProp: 'ID' , destObj: metaObj.mixins.unity.entity, destProp: 'ID' });
        }

        Ext.Object.each(me.editor.graph.model.cells, function(id,cell){
           var objectCode = cell.getAttribute('objectCode');
           if (!objectCode){
               return true;
           }
           linkmetaObj = $App.domainInfo.get(objectCode);
           if (!linkmetaObj){
                return true;
           }

            // other objects inherited from me
           if  (linkmetaObj.mixins && linkmetaObj.mixins.unity && /* linkmetaObj.mixins.unity.enabled && */
                linkmetaObj.mixins.unity.entity === objCode){
               lnkType = { linkType: 'inheritage', srcObj: objectCode,
                   srcProp: 'ID' , destObj: objCode, destProp: 'ID' };
               me.editor.graph.addEdge(me.createEdge(lnkType), null, cell, vertex  );
           }


           // obj link to other
           if (objLinks[objectCode]){
              Ext.Array.each(objLinks[objectCode], function(val){
                   me.editor.graph.addEdge(me.createEdge(val), null, vertex, cell );
              }, me );
           }
            // other objs link to me (if is not inner link)
            if (objectCode !== objCode){
                linkmetaObj.eachAttribute(function(attr, attrCode){
                    if (Ext.String.startsWith(attrCode, 'mi_', true )){
                        return true;
                    }
                    if (attr.dataType === 'Entity' && attrCode.toUpperCase() !== 'ID' && attr.associatedEntity === objCode){
                        lnkType = { linkType: 'relation', srcObj: objectCode,
                            srcProp: attrCode , destObj: objCode, destProp: 'ID' };
                        me.editor.graph.addEdge(me.createEdge(lnkType), null, cell, vertex  );
                    }
                }, me);
            }
        }, me);

    },

    createEdge: function(param){
        /*
        var e = new mxCell('');
        e.setEdge(true);
        var geo = new mxGeometry();
        geo.relative = true;
        e.setGeometry(geo);
        e.setValue(param);
        e.setStyle('edgeStyle=entityRelationEdgeStyle');
        */
        var assoc, doc, node;

        doc = mxUtils.createXmlDocument();
        node = doc.createElement('ubObject');

        node.setAttribute('linkType', param.linkType );
        node.setAttribute('srcObj', param.srcObj );
        node.setAttribute('srcProp', param.srcProp );
        node.setAttribute('destObj', param.destObj );
        node.setAttribute('destProp', param.destProp );

        if (param.linkType === 'relation'){
            assoc = new mxCell(node , new mxGeometry(0, 0, 0, 0),
                'endArrow=open;endSize=10;startArrow=diamondThin;startSize=10;startFill=0;edgeStyle=orthogonalEdgeStyle;' );
            assoc.geometry.setTerminalPoint(new mxPoint(0, 0), true);
            assoc.geometry.setTerminalPoint(new mxPoint(80, 0), true);
            assoc.geometry.setTerminalPoint(new mxPoint(160, 0), false);
            assoc.edge = true;

            /*
            sourceLabel = new mxCell('0..n', new mxGeometry(-1, 0, 0, 0), 'resizable=0;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10');
            sourceLabel.geometry.relative = true;
            sourceLabel.setConnectable(false);
            sourceLabel.vertex = true;
            assoc.insert(sourceLabel);

            targetLabel = new mxCell('1', new mxGeometry(1, 0, 0, 0), 'resizable=0;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10');
            targetLabel.geometry.relative = true;
            targetLabel.setConnectable(false);
            targetLabel.vertex = true;
            assoc.insert(targetLabel);
            */
        }
        // segmentEdgeStyle orthogonalEdgeStyle
        if (param.linkType === 'inheritage'){
            assoc = new mxCell(node, new mxGeometry(0, 0, 0, 0), 'endArrow=block;endSize=10;endFill=0;startArrow=oval;startFill=0;startSize=7;edgeStyle=segmentEdgeStyle;fillColor=#0B31D9;strokeColor=#0B31D9;');
            assoc.geometry.setTerminalPoint(new mxPoint(0, 0), true);
            assoc.geometry.setTerminalPoint(new mxPoint(80, 0), true);
            assoc.geometry.setTerminalPoint(new mxPoint(160, 0), false);
            assoc.edge = true;
        }

        //assoc.setAttribute('label', param.attributeFrom);
        return assoc;
    },

    initObjectTree: function(){

        var ftree, me = this, store, detail = [], subsystems = {};

        $App.domainInfo.eachEntity(function(objVal, objName){
            var subsystem = objVal.modelName;
//            var pos = objName.indexOf('_'), subsystem = objName;
//            if (pos >= 0){
//                subsystem = objName.substr(0,pos);
//            }
            var parentNode = subsystems[subsystem];
            if (!parentNode){
                parentNode  = {text: subsystem , expanded: false, children: [], icon : null};
                subsystems[subsystem] = parentNode;
                detail.push(parentNode);
                parentNode.children.push({text: objName , leaf: true, icon : null, objCode: objName});
            } else {
                parentNode.children.push({text: objName , leaf: true, icon : null, objCode: objName});
            }
        }, me);

        ftree = { root: { expanded: true, children: detail } };
        store = Ext.create('Ext.data.TreeStore', ftree );




        me.objectTree =  Ext.create('Ext.tree.Panel',{
            title: UB.i18n('entities'),
            //maxHeight: 300,
            flex : 1,
            singleExpand : true,
            region:'north',
            store: store,
            rootVisible: false,
            listeners: {
                itemmousedown: function( sender, record, item, index, e /*, eOpts */){
                    if(record.isLeaf()){
                        var dragElm, dragE, itemE, fXY, basewindow ;
                        dragElm  = document.createElement('DIV');
                        basewindow = me.getContainer().getEl(); // me.up('basewindow').getEl();
                        document.documentElement.appendChild(dragElm);
                        dragE = Ext.get(dragElm);
                        itemE = Ext.get(item);
                        dragElm.style.border = '1px solid black';
                        dragElm.style.opacity = 1;
                        dragElm.style.backgroundColor = "white";
                        dragE.setWidth(itemE.getWidth());
                        dragE.setHeight(itemE.getHeight());
                        fXY = e.getXY();
                        dragE.position("absolute", basewindow.dom.style.zIndex + 2, fXY[0] , fXY[1]  );
                       me.dragDropObj = {objectName: record.get('text'), dragElm: dragElm };
                    }
                },
               scope: me
            }
        });

    },


    useBlobForData: true,

    getDataBlob: function(){
        var me = this;
        if (!me.useBlobForData){
            Ext.Error.raise('object does not use Blob');
        }
        return me.dataBlob;
    },


    updateDataBlob: function(inblob){
        var me = this;
        if (!me.useBlobForData){
            Ext.Error.raise('object does not use Blob');
        }
        //debugger;
        if (me.dataBlob && !Ext.isEmpty(this.objUrl)){
            window.URL.revokeObjectURL(this.objUrl);
        }
        me.data = null;
        me.dataBlob = inblob;
        me.objUrl = window.URL.createObjectURL(inblob);
        me.data =  me.objUrl;
    },

    onDestroy: function () {
        var me = this;
        //debugger;
        me.dataBlob = null;
        me.data = null;
        if (me.useBlobForData && !Ext.isEmpty(me.objUrl)){
            window.URL.revokeObjectURL(me.objUrl);
        }
        me.objUrl = null;
        this.callParent();
    },

    /**
     *
     * @param cfg
     * @returns(Promise)
     */
    setSrc: function(cfg) {
        var
            me = this,
            data = cfg.url,
            blobData = cfg.blobData,
            doInit;

        doInit = function(){
            me.changeFired = true;
            if(me.editor) {
                me.isInitEditor = true;
                try {
                    me.editor.open(me.objUrl);
                } finally {
                    me.isInitEditor = false;
                }
                me.validateDiagram();
                me.editor.setMode('select');
            }
            me.changeFired = false;
        };

        me.dataUrl = data;
        if (me.useBlobForData){
            if (blobData){
                me.updateDataBlob(blobData);
                doInit();
                return Q.resolve(true);
            } else {
                return $App.connection.get(me.dataUrl, {responseType: "arraybuffer"})
                    .then(function(response){
                        var blobData, pdfArray = response.data;
                        blobData = new Blob(
                            [pdfArray],
                            {type: cfg.contentType}
                        );
                        me.updateDataBlob(blobData);
                        doInit();
                    }).fail(function(reason){
                        if (reason.status === 404){
                            if (cfg.onContentNotFound){
                                cfg.onContentNotFound();
                            } else {
                                UB.showErrorWindow('<span style="color: red">' + UB.i18n('documentNotFound') + '<span/>');
                            }
                        }
                    });
            }

        } else {
            me.objUrl = me.dataUrl;
        }

        doInit();
        return  Q.resolve(true);
    },

    validateDiagram: function(){
       var me = this , graph = me.editor.graph,
           metaObj, metaObjRef, objCode, cellEdge,
           srcObj , srcProp , destObj , destProp, linkType, lnkProp,
           existProps = {}, deleteEdge, graphObject = {}, isInheritage, hasInheritage;


       Ext.Object.each(me.editor.graph.model.cells, function(id,cell){
                objCode = cell.getAttribute('objectCode');
                if (!objCode){
                    return true;
                }
                graphObject[objCode.toLowerCase()] = cell;
       });

       Ext.Object.each(me.editor.graph.model.cells, function(id,cell){
            objCode = cell.getAttribute('objectCode');
            if (!objCode){
                return true;
            }
            metaObj = $App.domainInfo.get(objCode);
           // удаляем устаревшие элементы в схеме
           if (!metaObj){
                me.editor.graph.removeCells([cell], true);
                return true;
           }
           hasInheritage = false;
           for (var i = cell.getEdgeCount() - 1; i > -1; i--){
               cellEdge =  cell.getEdgeAt(i);
                //srcObj: , srcProp: , destObj: , destProp:
               srcObj =  cellEdge.getAttribute('srcObj') || '';
               srcProp =  cellEdge.getAttribute('srcProp') || '';
               destObj =  cellEdge.getAttribute('destObj') || '';
               destProp =  cellEdge.getAttribute('destProp') || '';
               linkType =  cellEdge.getAttribute('linkType') || 'relation';
               isInheritage = (linkType === 'inheritage');
               deleteEdge = false;

                switch (objCode){
                    case srcObj:
                        lnkProp = metaObj.attr(srcProp);
                        metaObjRef = $App.domainInfo.get(destObj);
                        if (isInheritage){
                            deleteEdge = !lnkProp ||
                                !(metaObj.mixins && metaObj.mixins.unity && /**metaObj.mixins.unity.enabled && **/
                                    metaObj.mixins.unity.entity === destObj );
                            hasInheritage = !deleteEdge;
                        } else {
                            deleteEdge = !lnkProp || (lnkProp.dataType !== 'Entity') || (srcProp.toUpperCase() === 'ID') ||
                                (lnkProp.associatedEntity.toLowerCase() !== destObj.toLowerCase() ) ||
                                (Ext.String.startsWith(srcProp.toLowerCase(), 'mi_', true ));
                        }
                        deleteEdge = deleteEdge || !(graphObject[destObj.toLowerCase()]);
                        if (lnkProp){
                           existProps[srcProp] = lnkProp;
                        }
                        break;
                    case destObj:
                        lnkProp = metaObj.attr(destProp);
                        deleteEdge = !lnkProp ;
                        deleteEdge = deleteEdge || !(graphObject[srcObj.toLowerCase()]);
                        metaObjRef = $App.domainInfo.get(srcObj);
                        break;
                    default : lnkProp = null; deleteEdge = true;
                }
               if (!metaObjRef){
                   deleteEdge = true;
               }
               if (deleteEdge){
                    graph.model.beginUpdate();
                    try
                    {
                        graph.cellsRemoved([cellEdge]);
                        //graph.fireEvent(new mxEventObject(mxEvent.REMOVE_CELLS,
                        //    'cells', cells, 'includeEdges', null));
                    }
                    finally
                    {
                        graph.model.endUpdate();
                    }
               }
            }
           // добавляем отсутствующие элементы в схеме
           if (!hasInheritage && metaObj.mixins && metaObj.mixins.unity &&
               /** metaObj.mixins.unity.enabled &&**/ graphObject[metaObj.mixins.unity.entity]){
               var val = { linkType: 'inheritage', srcObj: objCode, srcProp: 'ID' , destObj: metaObj.mixins.unity.entity, destProp: 'ID' };
               me.editor.graph.addEdge( me.createEdge(val), null, cell, graphObject[metaObj.mixins.unity.entity] );
           }


           Ext.Object.each(metaObj.attributes, function(attrCode, attr){
               if (Ext.String.startsWith(attrCode, 'mi_', true )){
                   return true;
               }
               if (attr.dataType === 'Entity' && (attrCode.toUpperCase() !== 'ID') &&  graphObject[attr.associatedEntity.toLowerCase()] &&
                    !existProps[attrCode]){
                   var val = { linkType: 'relation', srcObj: objCode, srcProp: attrCode , destObj: attr.associatedEntity, destProp: 'ID' };
                   me.editor.graph.addEdge( me.createEdge(val), null, cell, graphObject[attr.associatedEntity.toLowerCase()] );
               }
           });
        },me);

    },

    /**
     * return either window or panel in which we born
     * @return {Ext.container.Container}
     */
    getContainer: function(){
        return this.up('basewindow') || this.up('basepanel');
    },

    initMXEditor: function(){
        var
            config = mxUtils.load('resources/mxGraph/config/ubmetaeditor.xml' ).getDocumentElement(),
            me = this, editor, graph, tdom, div, mainWin;
        // workfloweditor.xml

        mainWin = me.getContainer();

        me.editor = editor = new mxEditor(config);
        graph = editor.graph;

        me.graphDivEl = Ext.get(me.containerId);
        div = me.graphDivEl.dom;
        editor.setGraphContainer(div);

        (new Ext.Element(editor.toolbar.toolbar.container)).up('div.mxWindow').remove();
        Ext.get(me.toolbarId).dom.appendChild(editor.toolbar.toolbar.container);

        // key handler
        /*
        editor.keyHandler.handler.target = mainWin.getEl().dom;
        */
        editor.keyHandler.handler.target = me.getEl().dom;
        mxEvent.addListener(editor.keyHandler.handler.target, 'keydown', editor.keyHandler.handler.keydownHandler);

        //graph.addListener(mxEvent.CELLS_REMOVED, Ext.bind(me.onCellRemoved,me));
        //graph.addListener(mxEvent.REMOVE_CELLS, Ext.bind(me.onCellRemove,me));
        editor.keyHandler.handler.isGraphEvent = function(evt){ return me.isGraphEvent( evt, this ); };

        editor.setModified = function (value)
        {
            editor.modified = value;
            if (value){
             me.graphChanged();
            }
        };


        graph.popupMenuHandler.factoryMethod =
            mxUtils.bind(this, function(menu, cell, evt)
            {
                var frmDom = mainWin.getEl().dom;
                menu.div.parent = frmDom;
                menu.zIndex = frmDom.style.zIndex + 1;
                menu.div.style.zIndex = frmDom.style.zIndex + 1;

                //me.getel().style.zIndex;
                return editor.createPopupMenu(menu, cell, evt);
            });


        me.initGraph();

        editor.graph.cellsDisconnectable = false;
        editor.graph.cellsCloneable = false;
        editor.graph.allowDanglingEdges = false;
        editor.graph.disconnectOnMove = false;

        editor.showOutline();
        var el = (new Ext.Element(editor.outline.div)).down('div.mxWindowPane');
        Ext.get(me.outlineId).appendChild( el );
        editor.outline.setVisible(false); //MPV - hide native outline control

        editor._showTasks = editor.showTasks;
        if (editor.tasks){
            editor.tasks.destroy();
            editor.tasks = null;
        }
        editor.showTasks = Ext.Function.bind(me.showTasks, me);
        editor.refreshTasks = Ext.Function.bind(me.refreshTasks, me);

        editor.properties = null;
        editor.showProperties = Ext.Function.bind(me.showProperties, me);
        editor.hideProperties = Ext.Function.bind(me.hideProperties, me);

        /*
        editor.addAction('toggleOutline', function(editor)
        {
            if (editor.outline === null) {
                editor.showOutline();
            } else {
                editor.outline.setVisible(!editor.outline.isVisible());
            }
        });
        */

        editor.addAction('edit', function(editorprm, cell /*, evt*/){
            if (cell){
              var objectCode = cell.getAttribute('objectCode');
              if (objectCode){
                 me.showEntityForm(objectCode);
              } else {
                  if (editor.graph.isEnabled() &&
                      editor.graph.isCellEditable(cell))
                  {
                      editor.graph.startEditingAtCell(cell);
                  }
              }
            }
        });

        graph.getTooltipForCell = me.getTooltipForCell;
        graph.tooltipHandler.zIndex = mainWin.getEl().dom.style.zIndex + 1;


        graph.setResizeContainer(true);
        //graph.setEnabled(false);

        // Enables rubberband selection
        /*jshint nonew: false */
        new mxRubberband(graph);
        /*jshint nonew: true */
        graph.setPanning(true);
        graph.setTooltips(true);

        if( me.objUrl) {
            me.isInitEditor = true;
            try {
            me.editor.open(me.objUrl);
            } finally {
                me.isInitEditor = false;
            }
        }

    },


    showEntityForm: function(entityCode){
        var me = this, storeData, mixData, entity, edtAttr, edtMixin, onTextFieldChange, gridProp;
        if (!me.editEntityFrm){

            /*
            Ext.define('M1Attributes',{
                extend: 'Ext.data.Model',
                fields: [
                    {name :'code', type: 'string'},
                    {name :'caption',type: 'string'},
                    {name :'dataType',type: 'string'},
                    {name :'associatedEntity',type: 'string'},
                    {name :'accessType',type: 'string'},
                    {name :'allowNull',type: 'string'},
                    {name :'isUnique',type: 'string'},
                    {name :'defaultValue', type: 'string'},
                    {name :'allowSort', type: 'string'}
                ]
            });*/
            storeData =  Ext.create('Ext.data.Store', {
                fields:['code', 'caption', 'size', 'dataType', 'associatedEntity', 'accessType', 'allowNull', 'isUnique', 'defaultValue', 'allowSort'],
                data : {attributes: [{}]}, // colData,
                proxy: {
                    type: 'memory',
                    reader: {
                        type: 'json',
                        root: 'attributes'
                    }
                }
            });
            mixData = Ext.create('Ext.data.Store', {
                fields:['icon', 'code', 'enabled'],
                proxy: {
                    type: 'memory',
                    reader: {
                        type: 'json',
                        root: 'mixins'
                    }
                }
            });
            me.editEntityFrm = Ext.create('UB.view.BaseWindow', {
                title: 'Entity',
                height: 500,
                width: 600,
                closeAction : 'hide',
                autoDestroy: false,
                modal: true,
                stateful: true,
                stateId: "ubMetaDiagrm_entityform",
                layout: { type: 'border' },
                //layout: {
                //    type: 'hbox',
                //    align: 'stretch'
                //},
                items: [
                    {
                        xtype: 'tabpanel',
                        deferredRender: false,
                        region:'center',
                        //split: true,
                        items: [
                            {
                              //height: 'auto',
                              title: UB.i18n('AsTаble'),
                              layout: { type: "border" },
                              items: [
                                  gridProp = Ext.create('Ext.grid.Panel',
                                  {
                                      //xtype:  'gridpanel',
                                      tbar: ['Search',{
                                          xtype: 'textfield',
                                          name: 'searchField',
                                          hideLabel: true,
                                          width: 200,
                                          listeners: {
                                              change: {
                                                  fn: function(){ onTextFieldChange(this); },
                                                  buffer: 100
                                              }
                                          }
                                      }
                                      ],
                                      region:'center',
                                      split: true,
                                      //height: '100%',
                                      //width: '100%',
                                      itemId: 'colGrid',
                                      flex: 1,
                                      title:  UB.i18n('Attributes'),
                                      store: storeData,
                                      columns: [
                                          { text:  UB.i18n('code'),  dataIndex: 'code' },
                                          { text:  UB.i18n('caption'),  dataIndex: 'caption' },
                                          { text:  UB.i18n('dataType'),  dataIndex: 'dataType' },
                                          { text:  UB.i18n('size'), width: 60,  dataIndex: 'size' },
                                          { text:  UB.i18n('associatedEntity'),  dataIndex: 'associatedEntity' },
                                          { text:  UB.i18n('accessType'),  dataIndex: 'accessType' },
                                          { text:  UB.i18n('allowNull'), width: 50,  dataIndex: 'allowNull' },
                                          { text:  UB.i18n('isUnique'),  width: 50, dataIndex: 'isUnique' },
                                          { text:  UB.i18n('defaultValue'),  dataIndex: 'defaultValue' },
                                          { text:  UB.i18n('allowSort'), width: 50, dataIndex: 'allowSort' }
                                      ],
                                      listeners:{
                                          select: function( grd, record/*, index, eOpts*/ ){
                                              var prop = me.editEntityFrmEditor.selEntity.attributes[record.get('code')];

                                              edtAttr.setValue( record.get('code') + ' = \r\n' + js_beautify(JSON.stringify(
                                                  prop.asJSON()
                                              )));
                                          },
                                          scope: me
                                      }
                                  }),
                                  {
                                      xtype: "ubcodemirror",
                                      mode: "application/x-javascript",
                                      itemId: 'edtAttr',
                                      width: 250,
                                      region:'east',
                                      showLineNumbers: false,
                                      split: true
                                  },
                                  {
                                      height: 120,
                                      region:'south',
                                      layout: { type: "border" },
                                      split: true,
                                      items:[
                                          {
                                              xtype:  'gridpanel',
                                              region:'center',
                                              split: true,
                                              itemId: 'mixGrid',
                                              flex: 1,
                                              title:  UB.i18n('Mixins'),
                                              store: mixData,
                                              columns: [
                                                  { text:  '-',  dataIndex: 'icon' },
                                                  { text:  UB.i18n('code'),  dataIndex: 'code' },
                                                  { text:  UB.i18n('enabled'),  dataIndex: 'enabled' }
                                              ],
                                              listeners:{
                                                    select: function( grd, record/*, index, eOpts */){
                                                        edtMixin.setValue( record.get('code') + ' = \r\n' + js_beautify(JSON.stringify(
                                                            me.editEntityFrmEditor.selEntity.mixins[record.get('code')])));
                                                    },
                                                    scope: me
                                              }
                                          },
                                          {
                                              xtype: "ubcodemirror",
                                              mode: "application/x-javascript",
                                              itemId: 'edtMixin',
                                              width: 250,
                                              region:'east',
                                              showLineNumbers: false,
                                              split: true
                                          }
                                      ]
                                  }

                                 ]
                            },
                            {
                                itemId: 'myEditor',
                                xtype: "ubcodemirror",
                                mode: "application/x-javascript",
                                title: UB.i18n('AsJSON'),
                                //flex: 1,
                                height: '100%',
                                style: {
                                    background: "white"
                                },
                                hideLabel: false /*, labelAlign: "top"*/
                            }
                        ]
                    }
                ]
            });
            me.editEntityFrmEditor = me.editEntityFrm.down('[itemId=myEditor]'); // me.editEntityFrm.getComponent('myEditor');
            me.editEntityFrmEditor.fColGrd =  me.editEntityFrm.down('[itemId=colGrid]'); //me.editEntityFrm.getComponent('columnGrid');
            me.editEntityFrmEditor.fmixGrid =  me.editEntityFrm.down('[itemId=mixGrid]');
            edtAttr = me.editEntityFrm.down('[itemId=edtAttr]');
            edtMixin = me.editEntityFrm.down('[itemId=edtMixin]');

            onTextFieldChange = function(tbox){
                var store = gridProp.getStore();
                var value = tbox.getValue();
                store.clearFilter(true);
                var valueftr = new RegExp(value, 'i');
                store.filter([
                    {filterFn: function(item) {

                        return valueftr.test(item.get("code")) ||
                            valueftr.test(item.get("caption")) ||
                            valueftr.test(item.get("dataType")) ||
                            valueftr.test(item.get("associatedEntity")) ||
                            valueftr.test(item.get("size")) ||
                            valueftr.test(item.get("accessType")) ;


                    }}
                ]);
            };



        }
        entity = $App.domainInfo.get(entityCode);
        me.editEntityFrmEditor.selEntity = entity;
        if (entity){
            me.editEntityFrmEditor.setValue( js_beautify(JSON.stringify(entity.asJSON())) );

            var colData = [];
            entity.eachAttribute( function(row, code){
                    var dat = Ext.clone(row);
                    dat.code = code;
                    colData.push(dat);
                }
            );
            var mixinData = [];
            Ext.Object.each(entity.mixins, function(code, row){
                    var dat = Ext.clone(row);
                    dat.code = code;
                    dat.icon = UB.ux.UBMetaDiagram.mixinPictures[code] || code;
                    mixinData.push(dat);
                }
            );


            me.editEntityFrm.setTitle(entityCode );
            me.editEntityFrmEditor.fColGrd.getStore().loadRawData( {attributes: colData}, false);
            me.editEntityFrmEditor.fmixGrid.getStore().loadRawData( {mixins: mixinData}, false);
            me.editEntityFrm.down('[itemId=edtAttr]').setValue(' ');
            me.editEntityFrm.down('[itemId=edtMixin]').setValue(' ');
                me.editEntityFrm.show();
        }
    },



    //    .isGraphEvent = function(evt)
    isGraphEvent: function(evt, handler){
        var source = mxEvent.getSource(evt);

        // Accepts events from the target object or
        // in-place editing inside graph
        if ((source === handler.target || source.parentNode === handler.target  ) ||
            (handler.graph.cellEditor !== null && source === handler.graph.cellEditor.textarea))
        {
            return true;
        }

        // in edit form
        var elt = source;
        while (elt !== null)
        {
            if (elt === handler.target)
            {
                return true;
            }

            elt = elt.parentNode;
        }


        // Accepts events from inside the container
        elt = source;

        while (elt !== null)
        {
            if (elt === handler.graph.container)
            {
                return true;
            }

            elt = elt.parentNode;
        }

        return false;
    },

    /*
    onCellRemove: function(graph, eventObject){
            var me = this, cells = eventObject.properties.cells;
            Ext.Array.each(cells,function(cell,index){
                var srcProp = cell.getAttribute('objectCode');
                if (!srcProp){
                    return true;
                }

                var edges = graph.getAllEdges([cell]);
                me.inProcessRemoveEdge = true;
                try
                {
                    graph.removeCells(edges);
                } finally {
                    me.inProcessRemoveEdge = false;
                }
                //graph.
                //Ext.Error.raise('It is impossible to remove the object links!');
            },me);
    },

    onCellRemoved: function(cells){
       Ext.Array.each(cells,function(cell,index){
           var objCode = cell.getAttribute('objectCode');
           if (!objCode){
               return true;
           }

       },this);
    },
     */

    getTooltipForCell:  function(cell){
        var attr, obj;
        if ((attr = cell.getAttribute('objectCode'))){
            obj = $App.domainInfo.get(attr);
            return obj.caption + ': ' + obj.description;
        }
        if ((attr = cell.getAttribute('srcProp'))){
            if ((cell.getAttribute('linkType') || 'relation') === 'relation'){
               return cell.getAttribute('srcObj') + '.' + attr + ' -> ' + cell.getAttribute('destObj') + '.' + cell.getAttribute('destProp');
            } else {
                return cell.getAttribute('srcObj') + ' inherit ' + cell.getAttribute('destObj') ;
            }
        }
    },


    graphChanged : function(){
        var me = this;
        //if (!me.changeFired){
            //var objVal = Ext.JSON.decode(this.initialValue);
            //objVal.forDirtyRecord= true;
            //this.initialValue = Ext.JSON.encode(objVal);
            if (!me.isInitEditor){
               this.fireEvent('change', this);
            }
        //}
        //me.changeFired =true;
    },

    initGraph: function(){
        var me = this, editor = me.editor, graph = editor.graph;
        // Enables rotation handle
        mxVertexHandler.prototype.rotationEnabled = true;
        // Enables managing of sizers
        mxVertexHandler.prototype.manageSizers = true;
        // Enables live preview
        mxVertexHandler.prototype.livePreview = true;

        // Enables guides
        //mxGraphHandler.prototype.guidesEnabled = true;
        graph.graphHandler.guidesEnabled = true;

        //graph.defaultLoopStyle = mxEdgeStyle.EntityRelation;
        graph.allowLoops = true;


        graph.isHtmlLabel = function(cell){
            var state = graph.view.getState(cell);
            var style = (state) ? state.style : graph.getCellStyle(cell);

            return style.html + '' === '1' || style.whiteSpace  + '' === 'wrap';
        };

        // HTML entities are displayed as plain text in wrapped plain text labels
        graph.cellRenderer.getLabelValue = function(state){
            var result = mxCellRenderer.prototype.getLabelValue.apply(graph.cellRenderer, arguments);

            if (state.style.whiteSpace + '' === 'wrap' && state.style.html + '' !== '1')
            {
                result = mxUtils.htmlEntities(result, false);
            }

            return result;
        };

        graph.convertValueToString = function(cell){
            if (Ext.isString(cell.value)) {
               return cell.value;
            } else {
               if ( !Ext.isEmpty(cell.getAttribute('objectCode')) ){
                  return me.generateObjHtml(cell);
               } else {
                  return cell.getAttribute('label');
               }
            }
        };


        //.removeCells = function(cells, includeEdges)
        var oldRemoveCells = graph.removeCells;
        graph.removeCells = function(cells, includeEdges){
            if (!cells){
               cells = graph.getDeletableCells(graph.getSelectionCells());
            }
            var index = cells.length - 1;

            while(index > -1){
                var srcProp = cells[index].getAttribute('srcProp');
                if (srcProp){
                    cells.splice(index,1);
                }
                index--;
            }
            oldRemoveCells.call(graph, cells, includeEdges );
        };

        /*
        var BaseisCellDeletable = graph.isCellDeletable;
        graph.isCellDeletable = function(cell)
        {
            var srcProp = cell.getAttribute('srcProp');
            return (!srcProp ||  me.inProcessRemoveEdge) && BaseisCellDeletable.call(graph, cell);
        };
        */

    },

    generateObjHtml: function(cell){
        var me = this, ubObj, objectCode, htmlt, isEntity, ftype;
        objectCode = cell.getAttribute('objectCode');
        if (!me.htmCache){
            me.htmCache ={};
        }
        //if ((htmlt = me.htmCache[objectCode + cell.getId()])){
        //  return htmlt;
        ///}

        //'<div style="overflow-y:scrol">', </div> e4e4e4 overflow-y:scrol opacity:1; width:100%; height:100%;
        htmlt = [
            //'<div style="overflow: hidden; font-size:12px; -moz-user-select: none; -webkit-user-select: none; -ms-user-select:none; user-select:none;">',
            //'<div style="width:100%; height=100px; overflow-y: hidden;" >',
            '<table width="100%" height="100%" style=" -moz-user-select: none; -webkit-user-select: none; -ms-user-select:none; user-select:none;" >',
            '<tr><td style="vertical-align: top;" ><div  >' , //  style="overflow: hidden; height:',cell.geometry.height - 25,'px "
            '<table style="width:100%;" >' ,
            '<tr><td colspan="2" style="text-align:center; background:#C1D3F5;padding:2px;">' , objectCode , '</td></tr>'
        ];
        ubObj = $App.domainInfo.get(objectCode);
        ubObj.eachAttribute(function(attr, attrCode){
            if (Ext.String.startsWith(attrCode, 'mi_', true )){
                return true;
            }
            isEntity = attr.dataType === 'Entity';
            ftype = attr.dataType === 'Enum' ? 'Enum ' + attr.enumGroup + '':
                (isEntity ? '<B>' + attr.associatedEntity + '</B>'  : attr.dataType );
            htmlt.push('<tr><td>',attrCode ,'</td><td style="padding:2px;">', ftype, '</td></tr>');
        });
        // Add mixins images
        var cellMixinImages = [];
        _.forEach(ubObj.mixins, function(mixinConfig, mixinName){
            if (mixinConfig.enabled){
                cellMixinImages.push(UB.ux.UBMetaDiagram.mixinPictures[mixinName] || mixinName);
            }
        });
        if (cellMixinImages.length){
            htmlt.push('<tr><td colspan="2" style="text-align:center; background:#C1D3F5;padding:2px;">' + cellMixinImages.join('&nbsp') + '</td></tr>');
        }

        htmlt.push('</table></div></td></tr></table>');
        /*
        htmlt.push('</table></div></td></tr><tr><td style="text-align:center;vertical-align: bottom;border-top: 1px solid #DBDBDB;" >');
        htmlt.push('mixins  ');
        Ext.Object.each(ubObj.mixins, function(mixinCode, mixin){
            htmlt.push( me.getMixinShortName(mixinCode)+ '  ' );
        },me);
        htmlt.push('</td></tr></table>');
        */
        return  htmlt.join('');
    },

    getMixinShortName: function(mixinCode){
        if (mixinCode === 'mStorage'){
          return 'S';
        } else
        {
          return mixinCode.substr(0,1).toUpperCase();
        }
    },

    refreshTasks: function (div){
        var me = this;
        if (this.tasks !== null)
        {
            //var div = this.tasks.content;
            //mxEvent.release(div);
            me.taskEl.innerHTML = '';
            me.taskEl.createTasks(div);
        }
    },

    showTasks: function(){
        var me = this, taskWnd, graph = me.editor.graph,
            taskPnl, taskEl;
        if (me.editor.tasks === null)
        {
            var div = document.createElement('div');
            div.style.padding = '4px';
            div.style.paddingLeft = '20px';
            //var w = document.body.clientWidth;

            taskPnl = Ext.widget('panel', {
                border: true,
                flex: 1
            });

            taskWnd = Ext.create('Ext.Window', {
                title: 'Tasks',
                width: 200,
                height: 400,
                closable: true,
                closeAction : 'hide',
                autoDestroy: false,
                layout: 'fit',
                items:[
                    taskPnl
                ]
            }).show();
            me.taskWnd = taskWnd;
            taskWnd.ownerCt = me;
            taskWnd.registerWithOwnerCt();
            var tdom = taskPnl.getEl().dom;
            document.getElementById(tdom.id + '-innerCt').appendChild(div);
            me.taskEl = taskEl = div;


            /*
             var wnd = new mxWindow(
             mxResources.get(this.tasksResource) ||
             this.tasksResource,
             div, w - 220, this.tasksTop, 200);
             wnd.setClosable(true);
             wnd.destroyOnClose = false;
             */
            // Installs a function to update the contents
            // of the tasks window on every change of the
            // model, selection or root.
            var funct = mxUtils.bind(me, function(/*sender*/){
                //mxEvent.release(div);
                me.taskEl.innerHTML = '';
                me.editor.createTasks(me.taskEl);
            });

            graph.getModel().addListener(mxEvent.CHANGE, funct);
            graph.getSelectionModel().addListener(mxEvent.CHANGE, funct);
            graph.addListener(mxEvent.ROOT, funct);

            me.editor.tasks = taskWnd;
            //debugger;
            me.editor.createTasks(div);
        }

        me.taskWnd.show();
    },

    showProperties:  function (cell){
        var
            me = this,
            editor = me.editor;
        cell = cell || editor.graph.getSelectionCell();

        // Uses the root node for the properties dialog
        // if not cell was passed in and no cell is
        // selected
        if (cell === null)
        {
            cell = editor.graph.getCurrentRoot();

            if (cell === null)
            {
                cell = editor.graph.getModel().getRoot();
            }
        }

        if (cell !== null)
        {
            // Makes sure there is no in-place editor in the
            // graph and computes the location of the dialog
            editor.graph.stopEditing(true);

//            var offset = mxUtils.getOffset(editor.graph.container);
//            var x = offset.x+10;
//            var y = offset.y;

            /*
             // Avoids moving the dialog if it is alredy open
             if (editor.properties !== null && !editor.movePropertiesDialog)
             {
             x = editor.properties.getX();
             y = editor.properties.getY();
             }

             // Places the dialog near the cell for which it
             // displays the properties
             else
             {
             var bounds = editor.graph.getCellBounds(cell);

             if (bounds != null)
             {
             x += bounds.x+Math.min(200, bounds.width);
             y += bounds.y;
             }
             }
             */
            // Hides the existing properties dialog and creates a new one with the
            // contents created in the hook method
            editor.hideProperties();
            var node = editor.createProperties(cell);

            if (node !== null)
            {
                // Displays the contents in a window and stores a reference to the
                // window for later hiding of the window

                var propPnl = Ext.widget('panel', {
                    border: true,
                    flex: 1
                });

                var propWnd = Ext.create('Ext.Window', {
                    title: 'Properties',
                    width: 250,
                    height: 400,
                    closable: false,
                    //closeAction : 'hide',
                    //autoDestroy: false,
                    layout: 'fit',
                    items:[
                        propPnl
                    ]
                }).show();
                //me.taskWnd = taskWnd;
                propWnd.ownerCt = me;
                propWnd.registerWithOwnerCt();
                var tdom = propPnl.getEl().dom;
                document.getElementById(tdom.id + '-innerCt').appendChild(node);
                editor.properties = propWnd;

                //this.properties = new mxWindow(mxResources.get(this.propertiesResource) ||
                //    this.propertiesResource, node, x, y, this.propertiesWidth, this.propertiesHeight, false);
                //this.properties.setVisible(true);
            }
        }
    },

    hideProperties:  function (){
        var
            me = this,
            editor = me.editor, cell;
        if (editor.properties !== null)
        {
            editor.properties.close();
            editor.properties = null;
        }
    },

    beforeDestroy: function(){
        var
            me = this;
        if (me.editor) { me.editor.destroy();}
        if (me.tasksWnd) { me.tasksWnd.destroy(); }

        me.editEntityFrmEditor = null;
        if (me.editEntityFrm) {me.editEntityFrm.destroy();}
        Ext.EventManager.un(document, 'onmouseup', me.onmouseUpFunc ,me);
        Ext.EventManager.un(document, 'onmousemove', me.onmousemoveFunc ,me);

        me.callParent(arguments);
    },

    actionHandler: function(sender){
        this.editor.execute(sender.mxAction);
    },

    getValue: function(){
        var me = this, enc = new mxCodec(),
        data = enc.encode(me.editor.graph.getModel());
        return  mxUtils.getXml(data);

        //return this.initialValue;
    },

    resetOriginalValue : function(){
        return null;
    }



}
);