/*

Siesta 5.1.0
Copyright(c) 2009-2018 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license

*/
Ext.define('Siesta.Project.Browser.UI.DomContainer', {
    extend : 'Ext.Panel',
    alias  : 'widget.domcontainer',

    requires                : [
        'Siesta.Project.Browser.UI.ComponentInspector'
    ],

    cls                     : 'siesta-domcontainer',
    header                  : false,
    collapsible             : true,
    animCollapse            : false,
    padding                 : 10,

    test                    : null,
    testListeners           : null,

    scaleToFit              : true,
    scaleToFitMode          : 'full',

    maintainViewportSize    : true,
    canManageDOM            : true,
    suspendAfterLayoutAlign : false,

    inspector               : null,
    inspectorConfig         : null,
    enableConsole           : true,

    trackMouse              : true,

    initComponent : function () {
        var me = this;

        this.testListeners  = []

        this.title          = Siesta.Resource('Siesta.Project.Browser.UI.DomContainer', 'title');

        //this.addEvents(
        //    'inspectionstart',
        //    'inspectionstop',
        //    'targethover',
        //    'targetselected'
        //)

        if (this.enableConsole) {
            Ext.apply(this, {
                dockedItems : this.consoleCt = {
                    xtype     : 'component',
                    dock      : 'bottom',
                    hidden    : true,
                    height    : 20,
                    cls       : 'domcontainer-console',
                    renderTpl : '<div><input type="text" /></div>'
                }
            });
        }

        this.callParent()

        this.on({
            afterlayout : this.onAfterLayout,
            expand      : this.onExpand,
            collapse    : this.onCollapse,

            scope : this
        });

        this.inspector = new Siesta.Project.Browser.UI.ComponentInspector(this.inspectorConfig);

        if (this.trackMouse) {
            this.inspector.on({
                start : this.onInspectionStart,
                stop  : this.onInspectionStop,
                scope : this
            });
        }

        this.relayEvents(this.inspector, ['start', 'stop'], 'inspection')

        this.enableBubble('statechange');
    },


    afterRender : function () {
        this.callParent(arguments);

        if (this.enableConsole) {
            var input = this.consoleInput = this.el.down('.domcontainer-console input');

            this.inspector.on('targethover', function (dc, cmp) {
                input.dom.value = 'Ext.getCmp("' + cmp.id + '").';
            });

            this.inspector.on('targetselected', function (dc, cmp) {
                input.focus(true);
            });

            input.on({
                keyup : function (e, t) {
                    var val = input.dom.value;

                    if (e.getKey() === e.ENTER && val) {
                        var frame = this.getIFrame();

                        try {
                            var retVal = frame.contentWindow.eval(val);
                            if (window.console) {
                                console.log(retVal);
                            }
                        } catch (e) {
                            window.console && console.log(e.message);
                        }
                    }
                },
                scope : this
            });
        }
    },


    setScaleToFit : function (scaleToFit, mode) {
        if (this.scaleToFit == scaleToFit && (!mode || this.scaleToFitMode == mode)) return

        this.scaleToFit = scaleToFit
        if (mode) this.scaleToFitMode = mode

        if (scaleToFit) {
            this.doScaleToFit();
        } else {
            this.undoScaleToFit();
        }

        this.fireEvent('statechange')
    },


    doScaleToFit : function (isInitial) {
        var iframe      = this.getIFrame();
        if (!iframe) return

        var mode            = this.scaleToFitMode
        var scrollbarSize   = Ext.getScrollbarSize()

        var innerWrapper    = iframe.parentNode
        var scrollCanvas    = innerWrapper.parentNode
        var wrapper         = scrollCanvas.parentNode

        var availableWidth  = this.body.dom.offsetWidth - (mode === 'width' ? scrollbarSize.width : 0)
        var availableHeight = this.body.dom.offsetHeight - (mode === 'height' ? scrollbarSize.height : 0)

        var contentWidth    = iframe.offsetWidth
        var contentHeight   = iframe.offsetHeight

        var widthScale      = availableWidth / contentWidth
        var heightScale     = availableHeight / contentHeight

        var scale           = mode === 'width' ? widthScale :
                              mode === 'height' ? heightScale :
                              Math.min(widthScale, heightScale)

        var leftWidth       = availableWidth - scale * contentWidth
        var leftHeight      = availableHeight - scale * contentHeight

        var left            = Math.max(0, leftWidth / 2)
        var top             = Math.max(0, leftHeight / 2)

        innerWrapper.style.transition    = isInitial ? 'none' : 'transform 0.3s'
        innerWrapper.style.transform     = 'translate(' + left + 'px, ' + top + 'px) scale(' + scale + ') ';

        scrollCanvas.style.width    = scale * contentWidth + 'px'
        scrollCanvas.style.height   = scale * contentHeight + 'px'

        this.removeScaleToFitClasses(wrapper)

        Ext.fly(wrapper).addCls('scaleToFit' + Joose.S.uppercaseFirst(mode));

        this.scrollTo(0, 0);

        // the "min" in the name should be removed
        var viewportSize        = this.test.scopeProvider.minViewportSize

        innerWrapper.style.width     = viewportSize.width + 'px'
        innerWrapper.style.height    = viewportSize.height + 'px'
    },


    removeScaleToFitClasses : function (wrapper) {
        Ext.fly(wrapper).removeCls('scaleToFitFull');
        Ext.fly(wrapper).removeCls('scaleToFitWidth');
        Ext.fly(wrapper).removeCls('scaleToFitHeight');
    },


    undoScaleToFit : function () {
        var iframe                  = this.getIFrame();
        if (!iframe) return

        var innerWrapper            = iframe.parentNode
        var scrollCanvas            = innerWrapper.parentNode

        this.removeScaleToFitClasses(scrollCanvas.parentNode)

        innerWrapper.style.transform    = ''
        innerWrapper.style.width        = iframe.style.width
        innerWrapper.style.height       = iframe.style.height
        innerWrapper.style.left         = ''
        innerWrapper.style.top          = ''

        scrollCanvas.style.width        = iframe.style.width
        scrollCanvas.style.height       = iframe.style.height
    },


    setCanManageDOM : function (value) {
        this.canManageDOM = value

        if (value && !this.hidden) this.alignIFrame()
    },


    getIFrameWrapper : function (force) {
        var test = this.test;

        if (test)
            return (this.canManageDOM || force) && test.scopeProvider && test.scopeProvider.wrapper || null
        else
            return null;
    },


    getIFrame : function (force) {
        var test = this.test;

        if (test)
            return (this.canManageDOM || force) && test.scopeProvider && test.scopeProvider.iframe || null
        else
            return null;
    },


    onAfterLayout : function () {
        if (!this.suspendAfterLayoutAlign) this.alignIFrame();
    },


    alignIFrame : function (force, isInitial) {
        var wrapper = this.getIFrameWrapper(force);

        if (!this.isFrameVisible() || !wrapper) return

        Ext.fly(wrapper).removeCls('tr-iframe-hidden')
        Ext.fly(wrapper).removeCls('tr-iframe-forced')

        var box = this.body.getBox()

        Ext.fly(wrapper).setBox(box)

        if (!this.maintainViewportSize) {
            Ext.fly(this.getIFrame(force)).setSize(this.body.getSize())
        }

        if (this.scaleToFit)
            this.doScaleToFit(isInitial)
        else
            this.undoScaleToFit()

        var test = this.test

        test && test.fireEvent('testframeshow')
    },


    onCollapse : function () {
        this.hideIFrame();
    },


    onExpand : function () {
        this.alignIFrame();
    },


    hideIFrame : function () {
        var iframe = this.getIFrameWrapper()

        iframe && Ext.fly(iframe).setStyle({
            left : '-10000px',
            top  : '-10000px'
        })

        var test = this.test

        test && test.fireEvent('testframehide')
    },


    isFrameVisible : function () {
        return !(this.hidden || this.collapsed)
    },


    showTest : function (test) {
        this.stopInspection();

        if (this.test) {
            Joose.A.each(this.testListeners, function (listener) {
                listener.remove()
            })

            this.testListeners = []

            this.hideIFrame()
        }

        this.test = test

        this.testListeners = [
            test.on('testfinalize', this.onTestFinalize, this)
        ]

        // when starting the test with forcedIframe - do not allow the assertion grid to change the location of the iframe
        // (canManageDOM is set to false)
        this.setCanManageDOM(!test.hasForcedIframe())

        this.alignIFrame(false, true);
    },


    onTestFinalize : function (event, test) {
        this.setCanManageDOM(true)

        // this prevents project from hiding the iframe, because "test.hasForcedIframe()" will return null
        // we've moved the iframe to the correct position, and it can never be "forced" again anyway
        if (this.isFrameVisible()) {
            test.forceDOMVisible    = false
            test.isDOMForced        = false
        }
    },


    destroy                 : function () {
        Ext.destroy(this.boxIndicator);
        this.boxIndicator = null;

        // just in case
        this.hideIFrame()

        Joose.A.each(this.testListeners, function (listener) {
            listener.remove()
        })

        this.test = null

        this.callParent(arguments)
    },

    // BEGIN Inspection related code
    // -----------------------------
    inspectedComponent      : null,
    inspectedComponentXType : null,
    boxIndicator            : null,

    inspecting : false,

    toggleInspectionMode : function (on) {
        if (!this.test) return;

        if (on) {
            this.startInspection();

        } else {
            this.stopInspection();
        }
    },


    startInspection : function (showConsole) {
        if (!this.test || !this.test.global) return;

        var wrap = Ext.get(this.getIFrameWrapper());

        this.inspector.start(this.test.global, wrap.down('.tr-iframe-wrapper-inner', true));

        if (this.trackMouse) {
            wrap.un('mouseout', this.onMouseLeave, this);
            wrap.on('mouseout', this.onMouseLeave, this);
        }

        if (this.enableConsole && showConsole !== false) {
            if (!(this.consoleCt instanceof Ext.Component)) {
                this.consoleCt = Ext.widget(this.consoleCt);
            }
            this.consoleCt.show();
        }
        this.addCls('inspection-mode');
    },


    stopInspection : function () {
        this.inspector.stop();

        this.removeCls('inspection-mode');

        if (this.enableConsole && this.consoleCt.rendered) {
            this.consoleCt.hide();
        }
    },


    onMouseLeave : function (e, t) {
        if (!this.el.contains(e.relatedTarget) && !Ext.fly(this.getIFrameWrapper()).contains(e.relatedTarget)) {
            this.stopInspection();
        }
    },


    onInspectionStart : function () {
        var wrap = Ext.get(this.getIFrameWrapper());

        if (wrap) {
            wrap.on('mouseout', this.onMouseLeave, this);
        }
    },


    onInspectionStop : function () {
        var wrap = Ext.get(this.getIFrameWrapper());

        if (wrap) {
            wrap.un('mouseout', this.onMouseLeave, this);
        }
    },


    clearHighlight : function () {
        this.stopInspection();
    },


    highlightTarget : function (target, content, offset) {
        var targetIsPoint = target instanceof Array;

        if (!this.inspector.active) {
            this.startInspection();
        }

        if (targetIsPoint) {
            offset = target;
            target = null;
        } else {
            target = this.test.normalizeElement(target, true, true, false);


            if (target) {
                // Detect if we're trying to highlight a hidden target, then skip the target
                if (!target.offsetWidth && !target.offsetHeight) {
                    target = offset = null;
                }
                else if (offset) {
                    offset = this.test.normalizeOffset(offset, $(target));
                }
            }
        }

        this.inspector.highlightTarget(target, content, offset);
    }
});