/**
 * @version 2
 * @file 
 * Creates the structure necessary to implement a custom widget automatic resize used in the Mobile Access.
 */

/**
* @namespace SmaCustomWidget
* @property             resizeManager                   - Used to manage automatic resize.
* @method               resizeManager.activate          - Activate the automatic resize.
*/

function _____x_____SmaCustomWidgetResizeManager() {
    var _observer = null,
        _translate = null,
        _resizeTimeout = null,
        _onChangeTimeout = null,
        _boundaries = null,
        _startPosition = null,
        _isActive = false,
        _proxy = null,
        _currentSize = null,
        _currentPosition = null,
        _onChange = function (m) {
            var i,
                mLen = m.length;

            for (i = 0; i < mLen; ++i) {
                var need_recalculate = false,
                    addedNodesLen = m[i].addedNodes.length,
                    removedNodesLen = m[i].removedNodes.length,
                    j;

                if (m[i].type == 'childList') {
                    for (j = 0; j < addedNodesLen; ++j) {
                        _checkBoundaries(m[i].addedNodes[j], true);
                        _observe(m[i].addedNodes[j]);
                    }

                    for (j = 0; j < removedNodesLen; ++j) {
                        if (_isBoundary(m[i].removedNodes[j])) {
                            need_recalculate = true;
                        }
                    }
                } else {
                    if (_isBoundary(m[i].target)) {
                        need_recalculate = true;
                    }
                }
                if (need_recalculate) {
                    if (_onChangeTimeout) {
                        clearTimeout(_onChangeTimeout);
                    }
                    _onChangeTimeout = setTimeout(_calculateBoundaries.bind(this), 100);
                }
            }
        },
        _calculateBoundaries = function (observe_elements) {
            var elements = document.getElementsByTagName('*'),
                len = elements.length,
                index = 0;

            _clearBoundaries();

            for (index = 0; index < len; ++index) {
                _checkBoundaries(elements[index], false);
                if (observe_elements) {
                    _observe(elements[index]);
                }
            }

            if (_boundaries == null) {
                _proxy.applyAutoSize(parseInt(_startPosition.l),
                    parseInt(_startPosition.t),
                    parseInt(_startPosition.w),
                    parseInt(_startPosition.h));

                _currentPosition = {
                    'l': _startPosition.l,
                    't': _startPosition.t,
                }

                _currentSize = {
                    'w': _startPosition.w,
                    'h': _startPosition.h,
                }
            }

            _onChangeTimeout = null;
        },
        _clearBoundaries = function () {
            _boundaries = null;
            if (!_translate) {
                _currentPosition = {
                    'l': _startPosition.l,
                    't': _startPosition.t,
                }

                _currentSize = {
                    'w': _startPosition.w,
                    'h': _startPosition.h,
                }
            }
        },
        _checkBoundaries = function (node, recursive) {
            var clientRect = null,
                needResize = false,
                childNodesLen = node.childNodes.length,
                i;

            if (!node ||
                !(node.parentNode) ||
                node.tagName == 'HTML' ||
                node.tagName == 'HEAD' ||
                node.tagName == 'BODY' ||
                node.tagName == 'SCRIPT' ||
                node.tagName == 'TITLE') {
                return;
            }

            if (node.getBoundingClientRect) {
                clientRect = node.getBoundingClientRect();
            } else {
                console.warn('CustomWidget: element has not getBoundingClientRect defined.');
                return;
            }

            if (!_boundaries) {
                _boundaries = {
                    't': {
                        'n': node,
                        'v': clientRect.top,
                    },
                    'l': {
                        'n': node,
                        'v': clientRect.left,
                    },
                    'r': {
                        'n': node,
                        'v': clientRect.right,
                    },
                    'b': {
                        'n': node,
                        'v': clientRect.bottom,
                    },
                };
            }

            if (!_translate) {
                _translate = {
                    'x': 0,
                    'y': 0,
                };
            }

            if (_boundaries.t.v + _translate.x > clientRect.top) {
                _boundaries.t.v = clientRect.top;
                _boundaries.t.n = node;
                needResize = true;
            }

            if (_boundaries.l.v + _translate.y > clientRect.left) {
                _boundaries.l.v = clientRect.left;
                _boundaries.l.n = node;
                needResize = true;
            }

            if (_boundaries.r.v <= clientRect.right) {
                _boundaries.r.v = clientRect.right;
                _boundaries.r.n = node;
                needResize = true;
            }

            if (_boundaries.b.v <= clientRect.bottom) {
                _boundaries.b.v = clientRect.bottom;
                _boundaries.b.n = node;
                needResize = true;
            }

            if (needResize) {
                if (_resizeTimeout) {
                    clearTimeout(_resizeTimeout);
                }
                _resizeTimeout = setTimeout(_resize.bind(this), 500);
            }

            if (recursive) {
                for (i = 0; i < childNodesLen; ++i) {
                    _checkBoundaries(node.childNodes[i], recursive);
                }
            }
        },
        _resize = function () {
            var updatePosition = {
                l: null,
                t: null,
                w: null,
                h: null,
            },
                previousTranslate = {
                    'x': _translate.x,
                    'y': _translate.y,
                };

            if (!_boundaries || !_translate) {
                return;
            }

            updatePosition.l = _calculateLeft();
            updatePosition.t = _calculateTop();

            _calculateTranslateX(updatePosition.l);
            _calculateTranslateY(updatePosition.t);

            updatePosition.w = _calculateWidth(previousTranslate.x);
            updatePosition.h = _calculateHeight(previousTranslate.y);

            document.documentElement.style.transform = 'translate(' + _translate.x + 'px ,' + _translate.y + 'px)';

            _proxy.applyAutoSize(parseInt(updatePosition.l),
                parseInt(updatePosition.t),
                parseInt(updatePosition.w),
                parseInt(updatePosition.h));

            _currentPosition = {
                'l': updatePosition.l,
                't': updatePosition.t,
            }

            _currentSize = {
                'w': updatePosition.w,
                'h': updatePosition.h,
            }

            _resizeTimeout = null;
        },
        _calculateLeft = function () {
            return Math.min(_currentPosition.l + _boundaries.l.v, _startPosition.l);
        },
        _calculateTop = function () {
            return Math.min(_currentPosition.t + _boundaries.t.v, _startPosition.t);
        },
        _calculateWidth = function (previousTranslateX) {
            var width = _boundaries.r.v - previousTranslateX + _translate.x,
                right;

            width = Math.max(width, _startPosition.w);
            if (_startPosition.w >= width && Math.abs(_startPosition.w - width) < _translate.x) {
                width += _translate.x;
            }

            right = width - _translate.x;
            if (right < _startPosition.w) {
                width += _startPosition.w - right;
            }
            return width;
        },
        _calculateHeight = function (previousTranslateY) {
            var height = _boundaries.b.v - previousTranslateY + _translate.y,
                bottom;

            height = Math.max(height, _startPosition.h);
            if (_startPosition.h >= height && Math.abs(_startPosition.h - height) < _translate.y) {
                height += _translate.y;
            }

            bottom = height - _translate.y;
            if (bottom < _startPosition.h) {
                height += _startPosition.h - bottom;
            }
            return height;
        },
        _calculateTranslateX = function (left) {
            if (_boundaries.l.v < 0) {
                if (_boundaries.l.v > 0 && _currentPosition.l >= 0) {
                    _translate.x -= _currentPosition.l - left
                } else {
                    _translate.x += _currentPosition.l - left;
                }
            } else {
                if (_translate.x > 0) {
                    _translate.x += Math.min(_boundaries.l.v, _translate.x) * -1;
                }
            }
        },
        _calculateTranslateY = function (top) {
            if (_boundaries.t.v < 0) {
                if (_boundaries.t.v > 0 && _currentPosition.t >= 0) {
                    _translate.y -= _currentPosition.t - top;
                } else {
                    _translate.y += _currentPosition.t - top;
                }
            } else {
                if (_translate.y > 0) {
                    _translate.y += Math.min(_boundaries.t.v, _translate.y) * -1;
                }
            }
        },
        _observe = function (node) {
            var childNodesLen = node.childNodes.length,
                i;

            _observer.observe(node, {
                attributes: true,
                childList: true,
                characterData: true,
            });

            for (i = 0; i < childNodesLen; ++i) {
                _observe(node.childNodes[i]);
            }
        },
        _isBoundary = function (node) {
            var index,
                child_count = node.childElementCount;
            if (!_boundaries) {
                return false;
            }

            if (_boundaries.t.n == node ||
                _boundaries.l.n == node ||
                _boundaries.r.n == node ||
                _boundaries.b.n == node) {
                return true;
            }

            for (index = 0; index < child_count; index++) {
                if (_isBoundary(node.childNodes[index]))
                    return true;
            }

            return false;
        };
    this.activate = function () {

        if (_isActive) {
            return;
        }

        _isActive = true;

        _proxy = window.SmaCustomWidget.proxy;

        _currentPosition = {
            'l': parseInt(_proxy.getPosition().l),
            't': parseInt(_proxy.getPosition().t),
        }
        _currentSize = {
            'w': parseInt(_proxy.getSize().w),
            'h': parseInt(_proxy.getSize().h),
        }
        _startPosition = {
            'l': _currentPosition.l,
            't': _currentPosition.t,
            'w': _currentSize.w,
            'h': _currentSize.h,
        }

        if (!_observer) {
            setTimeout(function () {
                if (typeof (MutationObserver) === 'undefined') {
                    console.warn('CustomWidget: MutationObserver is not defined.');
                } else {
                    _observer = new MutationObserver(_onChange);
                    if (_observer) {
                        _calculateBoundaries(true);
                    }
                }
            }.bind(this), 100);
        }
    };
}

if (typeof (window.SmaCustomWidget) === 'undefined') {
    /**
    * The SmaCustomWidget is the namespace used to encapsulate all the functionalities provided and required
    * to implement custom widgets.    
    * @namespace SmaCustomWidget    
    */
    window.SmaCustomWidget = {
    };
}

window.SmaCustomWidget.resizeManager = new _____x_____SmaCustomWidgetResizeManager();