/*-
 * ~~~~~~licensing~~~~~~
 * entaxy-management-plugin
 * ==========
 * Copyright (C) 2020 - 2023 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
var Entaxy;
(function (Entaxy) {
    Entaxy._module
        .component('entaxyProfileDiagram', {
            template:
                `
          <div id="leader-lines-container" class="profile-diagram-container">

            <div class="blocks-container">
              <div id="source-block" class="source-block" ng-if="$ctrl.inConnectors && $ctrl.inConnectors.length > 0">
                <span>source</span>
              </div>
              <div id="target-block" class="target-block" ng-if="$ctrl.outConnectors && $ctrl.outConnectors.length > 0">
                <span>target</span>
              </div>
            </div>

            <div class="profile-block" ng-click="$ctrl.goToProfileProperties()">
              <span class="profile-title">{{$ctrl.profileTitle}}</span>

              <entaxy-connectors-with-flow direction="in" connectors="$ctrl.inConnectors" profile-name="$ctrl.profileName"
                    edit-customization-point-fn="$ctrl.editCustomizationPoint" ng-if="$ctrl.inConnectors && $ctrl.inConnectors.length > 0">
              </entaxy-connectors-with-flow>

              <entaxy-connectors-with-flow direction="out" connectors="$ctrl.outConnectors" profile-name="$ctrl.profileName"
                    edit-customization-point-fn="$ctrl.editCustomizationPoint" ng-if="$ctrl.outConnectors && $ctrl.outConnectors.length > 0">
              </entaxy-connectors-with-flow>

            </div>

            <div class="blocks-container">
              <div id="integrations-in-block" class="integrations-block" ng-if="$ctrl.inConnectors && $ctrl.inConnectors.length > 0">
                <span>integrations</span>
              </div>
              <div id="default-route-block" class="default-route-block" ng-click="$ctrl.editDefaultRouteProperties()"
                    ng-if="$ctrl.inConnectors && $ctrl.inConnectors.length > 0">
                <span>default-route</span>
              </div>
              <div id="integrations-out-block" class="integrations-block" ng-if="$ctrl.outConnectors && $ctrl.outConnectors.length > 0">
                <span>integrations</span>
              </div>
            </div>
          </div>
        `,
            controller: entaxyProfileDiagramController
        })
        .name;

    function entaxyProfileDiagramController($scope, workspace, jolokia, $location, operationsService, entaxyService, $uibModal) {
        'ngInject';

        var ctrl = this;
        let selectedMBean = workspace.getSelectedMBean();

        ctrl.$onInit = function () {

            ctrl.profileName = jolokia.getAttribute(selectedMBean.objectName, 'Name')
            ctrl.profileTitle = 'Profile: ' + ctrl.profileName;

            populateConnectors();
        }

        function populateConnectors() {
            let inConnectors = [];
            let outConnectors = [];

            if (selectedMBean && selectedMBean.isFolder) {
                let connectorMbeans = entaxyService.getAllChildMBeansByRuntimeType(selectedMBean, 'entaxy.runtime.connector');

                connectorMbeans.forEach((mbean) => {
                    let direction = jolokia.getAttribute(mbean.objectName, 'Direction');
                    let connector = {
                        name: jolokia.getAttribute(mbean.objectName, 'Name'),
                        mbeanName: mbean.objectName
                    };
                    if (direction === 'in') {
                        inConnectors.push(connector);
                    } else if (direction === 'out') {
                        outConnectors.push(connector);
                    }
                });

                ctrl.inConnectors = inConnectors.sort(Entaxy.compareBy('name'));
                ctrl.outConnectors = outConnectors.sort(Entaxy.compareBy('name'));
            }
        }

        // sets top margin for source, target, default-route and integration blocks
        setTimeout(function () {
            let sourceBlockMarginPlusHeight = 0;
            let defaultRouteBlockMarginPlusHeight = 0;
            if (ctrl.inConnectors && ctrl.inConnectors.length > 0) {
                let padding = 10;
                let integrationsInBlock = document.getElementById("integrations-in-block");
                let inFlowBlock = document.getElementById("in-flow-block");
                let sourceBlock = document.getElementById("source-block");
                let defaultRouteBlock = document.getElementById("default-route-block");

                let integrationsInBlockHeight = integrationsInBlock.offsetHeight;

                let sourceBlockMarginTop = getMarginFromTopElement(inFlowBlock, sourceBlock);
                let integrationsInBlockMarginTop = getMarginFromTopElement(inFlowBlock, defaultRouteBlock) -
                    inFlowBlock.offsetHeight + padding;
                let defaultRouteBlockMarginTop = inFlowBlock.offsetHeight - integrationsInBlockHeight - padding;

                sourceBlock.style.marginTop = sourceBlockMarginTop + 'px';
                integrationsInBlock.style.marginTop = integrationsInBlockMarginTop + 'px';
                defaultRouteBlock.style.marginTop = defaultRouteBlockMarginTop + 'px';

                sourceBlockMarginPlusHeight = sourceBlockMarginTop + sourceBlock.offsetHeight;
                defaultRouteBlockMarginPlusHeight = integrationsInBlockMarginTop + integrationsInBlockHeight +
                    defaultRouteBlockMarginTop + defaultRouteBlock.offsetHeight;
            }
            if (ctrl.outConnectors && ctrl.outConnectors.length > 0) {
                let outFlowBlock = document.getElementById("out-flow-block");
                let targetBlock = document.getElementById("target-block");
                let integrationsBlock = document.getElementById("integrations-out-block");

                let targetBlockMarginTop = getMarginFromTopElement(outFlowBlock, targetBlock) - sourceBlockMarginPlusHeight;
                let integrationsBlockMarginTop = getMarginFromTopElement(outFlowBlock, integrationsBlock) - defaultRouteBlockMarginPlusHeight;

                targetBlock.style.marginTop = targetBlockMarginTop + 'px';
                integrationsBlock.style.marginTop = integrationsBlockMarginTop + 'px';
            }
        }, 10);

        function getMarginFromTopElement(sourceBlock, targetBlock) {
            let sourceBlockOffsetTop = sourceBlock.offsetTop;
            let sourceBlockOffsetHeight = sourceBlock.offsetHeight;
            let targetBlockOffsetHeight = targetBlock.offsetHeight;

            let difference = Math.abs(targetBlockOffsetHeight - sourceBlockOffsetHeight);

            let marginTop = sourceBlockOffsetTop + difference / 2;

            return marginTop;
        }

        let lines = [];

        // draws lines for in-directed connector block
        $scope.$watchCollection('$ctrl.inConnectors', function (newValue, oldValue) {
            if (newValue && newValue.length > 0) {
                setTimeout(function () {

                    if (ctrl.inConnectors) {
                        let pathType = ctrl.inConnectors.length === 1 ? 'straight' : 'grid';

                        ctrl.inConnectors.forEach((connector) => {

                            lines.push(new LeaderLine(
                                LeaderLine.pointAnchor(document.getElementById('source-block'), {x: '100%', y: 12.5}),
                                document.getElementById(connector.name + '_pre-route'),
                                {color: 'green', size: 2, path: pathType, startSocketGravity: [15, 0]}
                            ));

                            lines.push(new LeaderLine(
                                document.getElementById(connector.name + '_pre-route'),
                                document.getElementById('in-flow_pre-route'),
                                {color: 'green', size: 2, path: 'grid', startSocketGravity: [10, 0]}
                            ));

                            lines.push(new LeaderLine(
                                document.getElementById(connector.name + '_response'),
                                LeaderLine.pointAnchor(document.getElementById('source-block'), {x: '100%', y: 44.5}),
                                {color: 'blue', size: 2, path: pathType, endSocketGravity: [35, 0]}
                            ));

                            lines.push(new LeaderLine(
                                document.getElementById('in-flow_response'),
                                document.getElementById(connector.name + '_response'),
                                {color: 'blue', size: 2, path: 'grid', startSocketGravity: [-10, 0]}
                            ));
                        });

                        lines.push(new LeaderLine(
                            document.getElementById('in-flow_pre-route'),
                            LeaderLine.pointAnchor(document.getElementById('default-route-block'), {x: 0, y: 12.5}),
                            {color: 'green', size: 2, path: 'straight'}
                        ));

                        lines.push(new LeaderLine(
                            LeaderLine.pointAnchor(document.getElementById('default-route-block'), {x: 0, y: 44.5}),
                            document.getElementById('in-flow_response'),
                            {color: 'blue', size: 2, path: 'straight'}
                        ));

                        lines.push(new LeaderLine(
                            LeaderLine.pointAnchor(document.getElementById('default-route-block'), {x: 32.5, y: 0}),
                            LeaderLine.pointAnchor(document.getElementById('integrations-in-block'), {x: 32.5, y: '100%'}),
                            {color: 'green', size: 2, path: 'straight'}
                        ));

                        lines.push(new LeaderLine(
                            LeaderLine.pointAnchor(document.getElementById('integrations-in-block'), {x: 64.5, y: '100%'}),
                            LeaderLine.pointAnchor(document.getElementById('default-route-block'), {x: 64.5, y: 0}),
                            {color: 'blue', size: 2, path: 'straight'}
                        ));
                    }

                }, 50);
            }
        });

        // draws lines for out-directed connector block
        $scope.$watchCollection('$ctrl.outConnectors', function (newValue, oldValue) {
            if (newValue && newValue.length > 0) {
                setTimeout(function () {

                    if (ctrl.outConnectors) {
                        let pathType = ctrl.outConnectors.length === 1 ? 'straight' : 'grid';

                        ctrl.outConnectors.forEach((connector) => {

                            lines.push(new LeaderLine(
                                document.getElementById(connector.name + '_pre-output'),
                                LeaderLine.pointAnchor(document.getElementById('target-block'), {x: '100%', y: 12.5}),
                                {color: 'green', size: 2, path: pathType, endSocketGravity: [35, 0]}
                            ));

                            lines.push(new LeaderLine(
                                document.getElementById('out-flow_pre-route'),
                                document.getElementById(connector.name + '_pre-output'),
                                {color: 'green', size: 2, path: 'grid', startSocketGravity: [-10, 0]}
                            ));

                            lines.push(new LeaderLine(
                                LeaderLine.pointAnchor(document.getElementById('target-block'), {x: '100%', y: 44.5}),
                                document.getElementById(connector.name + '_postprocess'),
                                {color: 'blue', size: 2, path: pathType, startSocketGravity: [15, 0]}
                            ));

                            lines.push(new LeaderLine(
                                document.getElementById(connector.name + '_postprocess'),
                                document.getElementById('out-flow_postprocess'),
                                {color: 'blue', size: 2, path: 'grid', startSocketGravity: [10, 0]}
                            ));
                        })

                        lines.push(new LeaderLine(
                            LeaderLine.pointAnchor(document.getElementById('integrations-out-block'), {x: 0, y: 12.5}),
                            document.getElementById('out-flow_pre-route'),
                            {color: 'green', size: 2, path: 'straight'}
                        ));

                        lines.push(new LeaderLine(
                            document.getElementById('out-flow_postprocess'),
                            LeaderLine.pointAnchor(document.getElementById('integrations-out-block'), {x: 0, y: 44.5}),
                            {color: 'blue', size: 2, path: 'straight'}
                        ));
                    }

                }, 50);
            }
        });

        // reposition of lines while container size is changed without changing window size
        document.querySelector('#leader-lines-container').handleResize = entry => {
            lines.forEach(line => line.position());
        }

        let resizeObserver = new ResizeObserver(entries => {
            for (let entry of entries) {
                if (entry.target.handleResize) {
                    entry.target.handleResize(entry);
                }
            }
        });

        resizeObserver.observe(document.querySelector('#leader-lines-container'));

        // reposition of lines on scroll event
        setTimeout(function () {
            let contentElement = document.getElementsByClassName("contents")[0];

            contentElement.onscroll = (event) => {
                lines.forEach(line => line.position());
            };
        }, 10);

        // reposition of lines while jmx-header is loaded after being empty
        $scope.$on('jmxTreeClicked', (event, selected) => {
            setTimeout(function () {
                if (selected.objectName === selectedMBean.objectName) {
                    lines.forEach(line => line.position());
                }
            }, 10);
        });

        $scope.$on('$destroy', function () {
            lines.forEach(line => line.remove());
            resizeObserver.disconnect();
        });

        ctrl.goToProfileProperties = function () {
            let currentLocationPath = $location.path();
            $location.path(currentLocationPath.replace('diagram', 'properties'));
        }

        ctrl.editDefaultRouteProperties = function () {

            let defaultRouteMbean = selectedMBean.findDescendant(child => {
                if (child.objectName) {
                    return child.objectName.endsWith('route=default');
                }
            });

            ctrl.editCustomizationPoint(defaultRouteMbean.objectName, 'doGetRouteConfig', [], 'routeContent');
        }

        ctrl.editCustomizationPoint = function (mbeanName, operationName, parameters, propertyName) {
            operationsService
                .executeOperation(mbeanName, {name: operationName}, parameters)
                .then(result => {
                    let args = getArgumentsFromConfig(JSON.parse(result));

                    let property = args.fields.find(property => property.name === propertyName);

                    openXmlEditor(property, args);

                }).catch(error => {
                Core.notification('danger', error, 5000);
                Entaxy.log.error(error);
            });
        }

        function getArgumentsFromConfig(configInfo) {
            let properties = Object.entries(configInfo.properties);
            properties = properties.map(property => {
                return {name: property[0], value: property[1]};
            });
            properties = properties.filter(property => property.name !== '__objectId');

            properties = properties.filter(property => property.name !== '##config');

            properties.unshift({name: 'objectId', value: configInfo.objectId});

            let factoryFolder = entaxyService.getDomainFolder()
                .findDescendant(child => child.title === configInfo.factoryId);
            let factory = {name: factoryFolder.title, mbeanName: factoryFolder.objectName};

            return {fields: properties, factoryId: factory};
        }

        function openXmlEditor(property, args) {

            $uibModal.open({
                component: 'entaxyXmlModal',
                resolve: {
                    xml: () => atob(property.value),
                    mode: () => Entaxy.MODAL_MODES.EDIT
                },
                size: 'xl',
                backdrop: 'static',
                windowTopClass: 'modal-top-margin-override'
            })
                .result.then(xml => {
                if (xml && xml.trim().length > 0) { // fixme
                    property.value = btoa(xml);
                    entaxyService.saveItem(args);
                } else {
                    Core.notification('danger', 'Value cannot be empty', 5000);
                }
            });
        }
    }

    entaxyProfileDiagramController.$inject = ['$scope', 'workspace', 'jolokia', '$location', 'operationsService', 'entaxyService', '$uibModal'];

})(Entaxy || (Entaxy = {}));
