(function(angular){
    'use strict';
    function _setTranslation(string){
        if (angular.isDefined(string)) {
            if (angular.isString(string)) {
                return {
                    translationId: string
                }
            } else if (angular.isObject(string)) {
                return string;
            }
        }
    }
    let BaseCtrlInject = ['$uibModalInstance', 'data', 'texts', 'classes', 'btnText'];
    class BaseCtrl {
        static get $inject(){
            return BaseCtrlInject;
        }
        constructor(...injectables){
            injectNg(injectables, BaseCtrlInject, this);
        }
        close(){
            this.$uibModalInstance.close();
        }
        $onInit(){
            this.msg = this.texts.msg;
            let { icon } = this.classes;
            this.classes.icon = angular.isDefined(icon) && angular.isString(icon) ? icon : 'fas fa-exclamation-triangle';
            this.header = _setTranslation(this.texts.header);
        }
    }
    let WaitCtrlInject = ['$uibModalInstance', '$timeout', 'texts', 'classes', 'progressInfo'];
    class WaitCtrl {
        static get $inject(){
            return WaitCtrlInject;
        }
        constructor(...injectables){
            injectNg(injectables, WaitCtrlInject, this);
        }
        close(){
            this.$uibModalInstance.close();
        }
        get getProgress(){
            let progress = this.progressInfo.progress / this.progressInfo.total * 100;
            if (progress === this.progressInfo.total) this.$timeout(() => this.close(), 250);
            return {
                width: `${progress}%`
            }
        }
        $onInit(){
            this.msg = this.texts.msg;
            let { total, progress } = this.progressInfo;
            this.progressInfo.total = angular.isDefined(total) ? total : 200;
            this.progressInfo.progress = angular.isDefined(progress) ? progress : 0;
            let { icon } = this.classes;
            this.classes.icon = angular.isDefined(icon) && angular.isString(icon) ? icon : 'fas fa-clock';
            this.header = _setTranslation(this.texts.header);
        }
    }
    let NewpopCtrlInject = ['data'];
    class NewpopCtrl {
        static get $inject(){
            return NewpopCtrlInject;
        }
        constructor(...injectables){
            injectNg(injectables, NewpopCtrlInject, this);
        }
        $onInit(){
            if (this.data.html) {
                this.msg = this.data.html
            } else if (this.data.msg) {
                this.msg = `<p style="margin-bottom: 0;" translate="{{$ctrl.data.msg}}"></p>`;
            } else {
                this.msg = '';
            }
            
            this.title = !isNU0(this.data.title) ? this.data.title : null;
            this.data = this.data.data;
        }
    }
    let ConfirmCtrlInject = ['$uibModalInstance', 'texts', 'data', 'classes'];
    class ConfirmCtrl {
        static get $inject(){
            return ConfirmCtrlInject;
        }
        constructor(...injectables){
            injectNg(injectables, ConfirmCtrlInject, this);
        }
        $onInit(){
            this.msg = this.texts.msg;
            this.classes.icon = angular.isDefined(icon) && angular.isString(icon) ? icon : 'fas fa-question-circle';
            this.header = _setTranslation(this.texts.header);
        }
        no(){
            this.$uibModalInstance.dismiss('no');
        }
        yes(){
            this.$uibModalInstance.close('yes');
        }
    }
    let ConfirmDeletionCtrlInject = ['$uibModalInstance', 'data', 'config'];
    class ConfirmDeletionCtrl {
        static get $inject(){
            return ConfirmDeletionCtrlInject;
        }
        constructor(...injectables){
            injectNg(injectables, ConfirmDeletionCtrlInject, this);
        }
        remove(row){
            removeFromArray(this.data, '$$hashKey', row.$$hashKey);
            if (isNU0(this.data)) this.$uibModalInstance.dismiss('no-data');
        }
        _get(row, value){
            return getSafe(row, value);
        }
        get hasRemovables(){
            return this.data.some(o => !o.notRemovable) && !this.config.loading;
        }
        no(){
            this.$uibModalInstance.dismiss('no');
        }
        yes(){
            this.$uibModalInstance.close(this.data.filter(o => !o.notRemovable));
        }
    }
    let StDialogsConfigInject = ['StDialogsDefaults'];
    class StDialogsConfig {
        static get $inject(){
            return StDialogsConfigInject;
        }
        constructor(...injectables){
            injectNg(injectables, StDialogsConfigInject, this);
        }
        set dialogs(dialogs){
            if (!angular.isObject(dialogs)) return;
            angular.merge(this.StDialogsDefaults.dialogs, dialogs);
        }
        set pop(pop){
            if (!angular.isObject(pop)) return;
            angular.merge(this.StDialogsDefaults.pop, pop);
        }
        set all(all){
            if (!angular.isObject(all)) return;
            angular.merge(this.StDialogsDefaults, all);
        }
        $get(){
            return this;
        }
    }
    let StDialogsServiceInject = ['StDialogsDefaults', '$uibModal', '$templateCache', '$uibModalStack', '$window', '$timeout'];
    class StDialogsService {
        static get $inject(){
            return StDialogsServiceInject;
        }
        constructor(...injectables){
            injectNg(injectables, StDialogsServiceInject, this);
        }
        mergeConfig(config, type){
            if (!type) type = 'dialogs';
            if (!isPlainObject(config)) config = {};
            return angular.merge({}, this.StDialogsDefaults[type], config);
        }
        getBaseConfig(config, templateUrl, controller, data){
            let resolve = {};
            if (angular.isObject(data)) {
                angular.forEach(data, (value, key) => {
                    resolve[key] = () => value;
                });
            } else {
                resolve = {
                    data: () => data
                }
            }
            return Object.assign({}, config, {
                templateUrl,
                controller,
                controllerAs: '$ctrl',
                resolve
            })
        }
        _applyRendered(modal, config){
            if (config.rendered) {
                if (angular.isFunction(config.rendered)) {
                    config.rendered.apply(config.rendered.this, config.rendered.values);
                } else if (isPlainObject(config.rendered)) {
                    let { rendered } = config;
                    modal.rendered.then(() => {
                        let el = this.$uibModalStack.getTop().value.modalDomEl,
                        selected = angular.element(el[0].querySelectorAll(rendered.selector));
                        if (rendered.apply in selected) {
                            selected[rendered.apply].apply(selected, rendered.values);
                        }
                    })
                }
            };
            return modal;
        }
        error(header, msg, config, data = {}){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                data: config.copy ? angular.copy(data) : data,
                classes: {
                    header: 'dialog-header-error',
                    text: 'danger',
                    icon: config.icon || 'fas fa-exclamation-triangle',
                    btnClose: 'btn-danger'
                },
                btnText: {
                    close: 'DIALOGS_CLOSE'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/base.html', BaseCtrl, resolve)), config);
        }
        wait(header, msg, progressInfo, config){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                progressInfo,
                classes: {
                    header: 'dialog-header-wait',
                    icon: config.icon || 'fas fa-clock'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/wait.html', WaitCtrl, resolve)), config);
        }
        notify(header, msg, config, data = {}){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                data: config.copy ? angular.copy(data) : data,
                classes: {
                    header: 'dialog-header-notify',
                    text: 'info',
                    icon: config.icon || 'fas fa-info-circle',
                    btnClose: 'btn-primary'
                },
                btnText: {
                    close: 'DIALOGS_OK'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/base.html', BaseCtrl, resolve)), config);
        }
        success(header, msg, config, data = {}){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                data: config.copy ? angular.copy(data) : data,
                classes: {
                    header: 'dialog-header-success',
                    text: 'success',
                    icon: config.icon || 'fas fa-check-circle',
                    btnClose: 'btn-success'
                },
                btnText: {
                    close: 'DIALOGS_CLOSE'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/base.html', BaseCtrl, resolve)), config);
        }
        infoModule(header, msg, config, data = {}){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                data: config.copy ? angular.copy(data) : data,
                classes: {
                    header: 'dialog-header-notify',
                    text: 'info',
                    icon: config.icon || 'fas fa-info-circle',
                    btnClose: 'btn-primary'
                },
                btnText: {
                    close: 'DIALOGS_CLOSE'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/base.html', BaseCtrl, resolve)), config);
        }
        confirm(header, msg, config, data = {}){
            config = this.mergeConfig(config);
            let resolve = {
                texts: {
                    header: angular.copy(header),
                    msg: angular.copy(msg),
                },
                data: config.copy ? angular.copy(data) : data,
                classes: {
                    header: 'dialog-header-confirm',
                    icon: config.icon || 'fas fa-question-circle',
                    btnClose: 'btn-default',
                    btnAccept: 'btn-primary'
                },
                btnText: {
                    accept: 'DIALOGS_YES',
                    close: 'DIALOGS_NO'
                }
            }
            return this._applyRendered(this.$uibModal.open(this.getBaseConfig(config, '/stDialogs/confirm.html', ConfirmCtrl, resolve)), config);
        }
        confirmDeletion(data, config = {}, options = {}){
            options = this.mergeConfig(options);
            if (!isPlainObject(config)) config = {};
            let resolve = {data, config};
            let base = this.getBaseConfig(options, config.templateUrl, ConfirmDeletionCtrl, resolve);
            return this._applyRendered(this.$uibModal.open(base), config);
        }
        create(config, data, options){
            options = this.mergeConfig(options);
            if (angular.isString(config)) {
                config = {
                    component: config
                }
            } else if (!isPlainObject(config)) throw new Error('Needs to be an Object | string');
            else if (!(config.template || config.templateUrl || config.controller || config.component)) throw new Error('Needs a component or controller/template');
            let modalOptions = Object.assign({}, options, config, {
                resolve: {
                    data: () => config.copy ? angular.copy(data) : data
                }
            });
            return this._applyRendered(this.$uibModal.open(modalOptions), config);
        }
        newpop(url, data, config){
            config = this.mergeConfig(config, 'pop');
            let wtemp = `<div class="modal-dialog dialogs" ${!config.noFit ? 'style="width: fit-content;"' : ''}><div class="modal-content" uib-modal-transclude></div></div>`,
            position = config.position;
            if (position) {
                position.top = !position.top ? 'auto' : position.top + 'px';
                if (config.posToShow === 'right') {
                    position.left = !position.left ? 'auto' : position.left + 'px';
                    position.right = 'auto';
                }
                if (config.posToShow === 'left') {
                    position.right = !position.right ? 'auto': position.right + 'px';
                    position.left = 'auto';
                }
                wtemp = `<div class="modal-dialog" style="margin-top:${position.top};margin-left:${position.left};margin-right:${position.right};${!config.noFit ? 'width: fit-content;' : ''}"><div class="modal-content" uib-modal-transclude></div></div>`
            }
            this.$templateCache.put('/stDialogs/newpopWindow.html', wtemp);
            let template = this.$templateCache.get(url),
            modalOptions = Object.assign({}, config, {
                windowTemplateUrl: '/stDialogs/newpopWindow.html',
                controller: NewpopCtrl,
                controllerAs: '$ctrl',
                resolve: {
                  data: () => config.copy ? angular.copy(data) : data
                },
                template: template || url
            });
            return this.$uibModal.open(modalOptions).rendered.then(() => {
                let modal = this.$uibModalStack.getTop(),
                modalDialog = angular.element(modal.value.modalDomEl[0].querySelector('.modal-dialog')),
                modalKey = modal.key;
                this.$window.addEventListener('resize', () => modalKey.dismiss('resize newpop dismiss'));
                if (config.closeOn === 'mouseleave') {
                    var tout;
                    if (config.timeout > 0) tout = this.$timeout(() => modalKey.dismiss('newpop dismiss'), config.timeout);
                    function mleave() {
                        modalKey.dismiss('newpop dismiss');
                    }
                    modalDialog.on('mousedown', () => {
                        modalDialog.off('mouseleave');
                    });
                    modalDialog.on('mouseenter', () => {
                        this.$timeout.cancel(tout);
                        modalDialog.on('mouseleave', mleave);
                    });
                }
            })
        }
    }
    NewpopDirective.$inject = ['StDialogs', '$document'];
    function NewpopDirective(StDialogs, $document){
        function getCoords(elem, appendTo) {
            var box = elem.getBoundingClientRect(),
                compare = document.documentElement,
                appendToBox = (appendTo ? document.querySelector('#'+appendTo).getBoundingClientRect() : null)
            if (appendTo) compare = document.querySelector('#'+appendTo);
            var ret = {
                top: (box.top - (box.height/2) - (appendToBox ? appendToBox.top : 0)).toFixed(2),
                left: (box.left - compare.offsetLeft + box.width + 15).toFixed(2),
                right: (compare.offsetWidth - box.left + box.width + compare.offsetLeft).toFixed(2),
                canRight: compare.offsetWidth - box.left >= 250,
                canLeft: box.left >= 250
            }
            return ret;
        }
        return {
            restrict: 'A',
            scope: {
                newpopData: '=',
                appendTo: '@newpopAppendTo',
                templateUrl: '<newpopTemplateUrl',
                msg: '@newpopMsg',
                html: '<newpopHtml',
                animation: '<newpopAnimation',
                closeOn: '@newpopCloseOn',
                openOn: '@newpopOpenOn',
                timeout: '@newpopTimeout',
                noFit: '<newpopNoFit',
                position: '@newpopPosition',
                title: '@newpopTitle'
            },
            link: function(scope, el) {
                var opts = {
                    data: angular.isDefined(scope.newpopData) ? scope.newpopData : {},
                    appendTo: angular.isDefined(scope.appendTo) ? scope.appendTo : null,
                    templateUrl: angular.isDefined(scope.templateUrl) ? scope.templateUrl : '/stDialogs/newpop.html',
                    msg: angular.isDefined(scope.msg) ? scope.msg : null,
                    html: angular.isDefined(scope.html) ? scope.html : null,
                    animation: angular.isDefined(scope.animation) ? scope.animation : true,
                    closeOn: angular.isDefined(scope.closeOn) ? scope.closeOn : null,
                    openOn: angular.isDefined(scope.openOn) ? scope.openOn : 'click',
                    timeout: angular.isDefined(scope.timeout) ? scope.timeout*1000 : 3000,
                    noFit: angular.isDefined(scope.noFit) ? scope.noFit : false,
                    positionToShow: angular.isDefined(scope.position) ? scope.position: 'right',
                    title: angular.isDefined(scope.title) ? scope.title : null
                };
                var data =  {
                    data: opts.data,
                    msg: opts.msg,
                    html: opts.html,
                    title: opts.title
                };
                el.on(opts.openOn, function(){
                    var appendToEl = null;
                    if (opts.appendTo) {
                        appendToEl = angular.element(document.querySelector('#'+opts.appendTo).querySelector('.modal-dialog .modal-content'));
                        if (!appendToEl) appendToEl = angular.element(document.querySelector('#'+opts.appendTo));
                    }
                    var position = getCoords(this, opts.appendTo);
                    if (opts.positionToShow === 'right' && !position.canRight) {
                        opts.positionToShow = 'left';
                    } else if (opts.positionToShow === 'left' && !position.canLeft) {
                        opts.positionToShow = 'right';
                    }
                    var options = {
                        position, 
                        appendTo: appendToEl,
                        animation: opts.animation,
                        closeOn: opts.closeOn,
                        timeout: opts.timeout,
                        noFit: opts.noFit,
                        posToShow: opts.positionToShow
                    }
                    StDialogs.newpop(opts.templateUrl, data, options);
                });
            }
        };
    }
    RunTemplateCache.$inject = ['$templateCache']
    function RunTemplateCache($templateCache){
        $templateCache.put('/stDialogs/base.html', `
            <div class="modal-header" ng-class="$ctrl.classes.header">
                <button type="button" class="close" ng-click="$ctrl.close()">&times;</button>
                <h4 class="modal-title">
                    <span ng-class="$ctrl.classes.icon"></span> 
                    <span translate="{{$ctrl.header.translationId}}" translate-values="{{$ctrl.header.values}}"></span>
                </h4>
            </div>
            <div class="modal-body text-{{$ctrl.classes.text}}" bind-html-compile="$ctrl.msg"></div>
            <div class="modal-footer">
                <button type="button" class="btn" ng-class="$ctrl.classes.btnClose" ng-click="$ctrl.close()" translate="{{$ctrl.btnText.close}}"></button>
            </div>
        `);
        $templateCache.put('/stDialogs/wait.html', `
            <div class="modal-header" ng-class="$ctrl.classes.header">
                <h4 class="modal-title">
                    <span ng-class="$ctrl.classes.icon"></span> 
                    <span translate="{{$ctrl.header.translationId}}" translate-values="{{$ctrl.header.values}}"></span>
                </h4>
            </div>
            <div class="modal-body">
                <span bind-html-compile="$ctrl.msg"></span>
                <div class="active progress progress-striped">
                    <div class="progress-bar progress-bar-info" ng-style="$ctrl.getProgress"></div>
                    <span class="sr-only">
                        <span ng-bind="$ctrl.progress"></span> 
                        <span translate="DIALOGS_PERCENT_COMPLETE"></span>
                    </span>
                </div>
            </div>
        `);
        $templateCache.put('/stDialogs/confirm.html', `
            <div class="modal-header" ng-class="$ctrl.classes.header">
                <button class="close" ng-click="$ctrl.no()" type="button">&times;</button>
                <h4 class="modal-title">
                    <span ng-class="$ctrl.classes.icon"></span> 
                    <span translate="{{$ctrl.header.translationId}}" translate-values="{{$ctrl.header.values}}"></span>
                </h4>
            </div>
            <div class="modal-body" bind-html-compile="$ctrl.msg"></div>
            <div class="modal-footer">
                <button class="btn" ng-class="$ctrl.classes.btnAccept" ng-click="$ctrl.yes()" type="button" translate="{{$ctrl.btnText.accept}}"></button>
                <button class="btn" ng-class="$ctrl.classes.btnClose" ng-click="$ctrl.no()" type="button" translate="{{$ctrl.btnText.close}}"></button>
            </div>
        `);
        $templateCache.put('/stDialogs/confirmDeletion.html', `
            <div class="dialog-header-confirm modal-header">
                <button class="close" ng-click="$ctrl.no()" type=button>&times;</button>
                <h4 class="modal-title">
                    <span class="fas fa-question-circle"></span> 
                    <span translate="ui-grid-st-confirm-deletion" translate-values="{{$ctrl.header.values}}"></span>
                </h4>
            </div>
            <div class="modal-body">
                <ul class="list-group">
                    <li class="list-group-item list-group-item-action ui-grid-st-deletion-animation" 
                        ng-repeat="row in $ctrl.data"
                        ng-class="{'not-removable': row.notRemovable}"
                        tooltip-enable="row.notRemovable" 
                        tooltip-popup-delay="378" 
                        uib-tooltip="{{$ctrl.config.verify.msg | translate}}">

                        <span ng-if="!$ctrl.config.noLabel" translate="{{$ctrl.config.label || $ctrl.config.confirmDeletion}}"></span>: 
                        <span ng-bind="$ctrl._get(row, $ctrl.config.confirmDeletion)"></span>
                        <div class="loading-bar-spinner-list pull-right" ng-if="$ctrl.config.loading">
                            <div class="spinner-icon"></div>
                        </div>
                        <a class="badge badge-danger" ng-if="$ctrl.config.deleteFromList">
                            <span class="fas fa-times" ng-click="$ctrl.remove(row)"></span>
                        </a>
                    </ul>
            </div>
            <div class="modal-footer">
                <button class="btn btn-primary" ng-click="$ctrl.yes()" type="button" ng-disabled="!$ctrl.hasRemovables" translate="DIALOGS_YES"></button>
                <button class="btn btn-default" ng-click="$ctrl.no()" type="button" translate="DIALOGS_NO"></button>
            </div>
        `);
        $templateCache.put('/stDialogs/newpop.html', `
          <div class="dialogs">
            <div class="panel panel-default" style="margin-bottom: 0px;">
              <div class="panel-body">
                <div bind-html-compile="msg"></div>
              </div>
            </div>
          </div>
        `)
    }
    angular.module('st.dialogs', ['ui.bootstrap.modal', 'pascalprecht.translate', 'ngAnimate'])
        .constant('StDialogsDefaults', {
            dialogs: {
                backdrop: true,
                keyboard: true,
                windowClass: 'dialogs-default',
                backdropClass: 'dialogs-backdrop-default',
                copy: true,
                windowTemplate: null,
                size: 'lg',
                animation: true
            },
            pop: {
                backdrop: true,
                keyboard: true,
                windowClass: 'dialogs-default',
                backdropClass: 'dialogs-newpop-backdrop',
                copy: true,
                wTemp: '<div class="dialogs modal-dialog"><div class="modal-content" uib-modal-transclude></div></div>',
                size: 'sm',
                animation: false,
                position: false,
                appendTo: false,
                closeOn: 'outsideClick',
                timeout: 3000,
                posToShow: 'right'
            }
        })
        .provider('StDialogsConfig', StDialogsConfig)
        .service('StDialogs', StDialogsService)
        .directive('dialogsNewpop', NewpopDirective)
        .run(RunTemplateCache)
}(window.angular))