// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
/**
 * @type {VisualizerView}
 */
define(['hp/presenter/visualizer/VisualizerPresenter',
    'hp/core/LinkTargetBuilder',
    'hp/view/SearchBoxView',
    'jquery',
    'hp/lib/jquery.hpSelect'],
function(presenter, linkTargetBuilder, searchBoxView) { "use strict";

    var VisualizerView = (function() {
      
        var CONTAINER = '#hp-visualizer';
        var CANVAS = CONTAINER + ' canvas';
        var SET = CONTAINER + ' .vis-set';
        var CONNECTION_COLOR = 'rgba(128, 128, 128, 0.5)';
        var SELECTED_CONNECTION_COLOR = 'rgba(66, 166, 212, 0.7)';
        
        var STATUS_MAP = {'UNKNOWN': 'unknown', 'OK': 'ok',
            'MAJOR': 'warning', 'CRITICAL': 'error',
            'uknown': 'unknown', 'ok': 'ok', 'warning': 'warning', 'error': 'error'};
      
        /**
         * Constructor
         */
        function VisualizerView() {
          
            var categories = {};
            var items = {};
            var activeCount = 0;
            var sequence = 1;
            var viewSequence = 1;
            var filteredSequence = 1;
            var selectedSequence = 0;
            var selectedItem = null;
            var connections = [];
            var context;
            var delayConnections = true;
            
            function clearConnections() {
                connections = [];
                if (context) {
                    var containerWidth = $(CONTAINER).width();
                    var containerHeight = $(CONTAINER).height();
                    $(CANVAS).attr('width', containerWidth).
                        attr('height', containerHeight);
                    context.clearRect(0, 0, containerWidth, containerHeight);
                }
            }
            
            function placeItems() {
                $.each(items, function (uri, item) {
                    if (item.filteredSequence !== viewSequence &&
                        item.selectedSequence !== viewSequence &&
                        item !== selectedItem) {
                        item.elem.css({bottom: 20});
                    } else {
                        item.elem.css(item.categoryPos);
                    }
                });
            }
            
            function placeCategories() {
                $.each(categories, function (name, category) {
                    if (category.filteredSequence !== viewSequence &&
                        category.selectedSequence !== viewSequence &&
                        ! category.selected) {
                        category.elem.css({bottom: 0});
                        $('.hp-count', category.elem).text('');
                    } else {
                        category.elem.css(category.pos);
                        if (viewSequence === selectedSequence) {
                            $('.hp-count', category.elem).
                                text(category.selectedItemCount);
                        } else {
                            $('.hp-count', category.elem).
                                text(category.filteredItemCount);
                        }
                    }
                });
            }
            
            function unsetSelection() {
                clearConnections();
                selectedItem = null;
                viewSequence = filteredSequence;
                placeCategories();
                placeItems();
            }
            
            function onClickContainer(ev) {
                if (ev.target == this || ev.target == $(CANVAS)[0]) {
                    $('.hp-pinned').removeClass('hp-pinned');
                    unsetSelection();
                }
            }
            
            function onClickItem() {
                var uri = $(this).attr('data-uri');
                var item = items[uri];
                $('.hp-pinned').removeClass('hp-pinned');
                $(this).addClass('hp-pinned');
                selectedItem = item;
                presenter.setSelectedItemUri(uri);
            }
            
            function onFilterCategories(ev, value) {
                //presenter.setFilterCategory(value);
                $("#hp-visualizer-categories .hp-value").
                    text('Filter categories');
            }
            
            function onFilterStatus(ev, value) {
                presenter.setFilterStatus(value);
            }
            
            function getOrAddCategory(categoryName, index) {
                var result = categories[categoryName];
                if (! result) {
                    result = {};
                    if (undefined !== index) {
                        result.index = index;
                    }
                    categories[categoryName] = result;
                    result.elem = $('<div data-category="' + categoryName +
                        '" class="vis-set"><label>' +
                        '<div class="hp-count"></div> ' +
                        linkTargetBuilder.categoryLabel(categoryName) +
                        '</label></div>');
                    $(CONTAINER).append(result.elem);
                }
                result.sequence = sequence;
                return result;
            }
            
            function useCategory(categoryName) {
                var category = categories[categoryName];
                if (category) {
                    category.sequence = sequence;
                }
                return category;
            }
            
            function getOrAddItem(indexResult, index, category) {
                var status, link;
                var result = items[indexResult.uri];
                if (! result) {
                    result = {category: category};
                    if (undefined !== index) {
                        result.index = index;
                    }
                    items[indexResult.uri] = result;
                    status = 'unknown';
                    if (indexResult.status && STATUS_MAP[indexResult.status]) {
                        status = STATUS_MAP[indexResult.status];
                    }
                    result.elem = $('<div></div>').addClass('vis-i').
                        attr('data-uri', indexResult.uri);
                    result.elem.append(
                        $('<div></div>').addClass('hp-status hp-status-' + status));
                    result.elem.append($('<label></label>').text(indexResult.name));
                    link = linkTargetBuilder.makeLink(indexResult.name, indexResult.uri);
                    if (link.indexOf('href') >= 0) {
                        result.elem.append(
                            $('<a></a>').addClass('hp-jump').
                                attr('href', $(link).attr('href')));
                    }
                    $(CONTAINER).append(result.elem);
                    result.elem.click(onClickItem);
                }
                result.sequence = sequence;
                return result;
            }
            
            function useItem(resource) {
                var item = items[resource.uri];
                if (item) {
                    item.sequence = sequence;
                    item.category.sequence = sequence;
                    if (sequence === filteredSequence) {
                        item.filteredSequence = sequence;
                        item.category.filteredItemCount += 1;
                        item.category.filteredSequence = sequence;
                    }
                    if (sequence === selectedSequence) {
                        item.selectedSequence = sequence;
                        item.category.selectedItemCount += 1;
                        item.category.selectedSequence = sequence;
                    }
                }
                return item;
            }
            
            function elementLocation(elem) {
                var elementPosition = elem.position();
                return {left: (elementPosition.left + (elem.width() / 2)),
                    top: (elementPosition.top + (elem.height() / 2))};
            }
            
            function connect(elem1, elem2, color) {
                var position1 = elementLocation(elem1);
                var position2 = elementLocation(elem2);
                context.beginPath();
                context.lineWidth = 1;
                context.lineCap = 'round';
                context.strokeStyle = color;
                context.moveTo(position1.left, position1.top);
                context.quadraticCurveTo(
                    position1.left + ((position2.left - position1.left) / 2),
                    position1.top,
                    position2.left, position2.top);
                context.stroke();
            }
            
            function drawConnections() {
                if (context) {
                    var containerWidth = $(CONTAINER).width();
                    var containerHeight = $(CONTAINER).height();
                    $(CANVAS).attr('width', containerWidth).
                        attr('height', containerHeight);
                    context.clearRect(0, 0, containerWidth, containerHeight);
                    $.each(connections, function (index, connection) {
                        connect(connection.parent, connection.child, connection.color);
                    });
                }
            }
            
            function linkParents(childItem, tree, level, connectTo) {
                var parentItem, category, connectedCategories = {};
                $.each(tree.parents, function (assocName, parents) {
                    $.each(parents, function (index, parent) {
                        parentItem = useItem(parent.resource);
                        if (parentItem) {
                            category = useCategory(parent.resource.category);
                            if (! connectedCategories[parent.resource.category]) {
                                connections.push({
                                    parent: category.elem,
                                    child: connectTo,
                                    color: (-1 !== level ? CONNECTION_COLOR :
                                        SELECTED_CONNECTION_COLOR)
                                });
                                connectedCategories[parent.resource.category] = true;
                            }
                            linkParents(parentItem, parent, level - 1, category.elem);
                        }
                    });
                });
            }
            
            function linkChildren(parentItem, tree, level, connectTo) {
                var childItem, category, connectedCategories = {};
                $.each(tree.children, function (assocName, children) {
                    $.each(children, function (index, child) {
                        childItem = useItem(child.resource);
                        if (childItem) {
                            category = useCategory(child.resource.category);
                            if (! connectedCategories[child.resource.category]) {
                                connections.push({
                                    parent: connectTo,
                                    child: category.elem,
                                    color: (1 !== level ? CONNECTION_COLOR :
                                        SELECTED_CONNECTION_COLOR)
                                });
                                connectedCategories[child.resource.category] = true;
                            }
                            linkChildren(childItem, child, level + 1, category.elem);
                        }
                    });
                });
            }
            
            function onTreeChange(tree) {
                sequence += 1;
                viewSequence = sequence;
                selectedSequence = sequence;
                
                $.each(categories, function (name, category) {
                    category.selectedItemCount = 0;
                });
                clearConnections();
                
                var item = useItem(tree.resource);
                
                linkParents(item, tree, -1, item.elem);
                linkChildren(item, tree, 1, item.elem);
                
                placeCategories();
                placeItems();
                
                // wait for animations before drawing
                setTimeout(function () {
                    drawConnections();
                }, (delayConnections ? 600 : 10));
            }
            
            function onFilterIndexResultsChange(indexResults) {
                var status;

                sequence += 1;
                viewSequence = sequence;
                filteredSequence = sequence;
                selectedSequence = 0;
                
                searchBoxView.setLocalSearchText(
                    indexResults.filter.getUserQuery());
                status = indexResults.filter.getProperty('status');
                if (status) {
                    $('#hp-visualizer-status').hpSelect('set', status);
                } else {
                    $('#hp-visualizer-status').hpSelect('set', 'all');
                }
                
                $.each(categories, function (name, category) {
                    category.filteredItemCount = 0;
                });
                clearConnections();
                delayConnections = true;
                
                $.each(indexResults.members, function (index, indexResult) {
                    useItem(indexResult);
                });
                
                placeCategories();
                placeItems();
            }
          
            function onCategoryIndexResultsChange(indexResults) {
                var item;
                var category = categories[indexResults.category];
                var containerHeight = $(CONTAINER).height();
                var set = $('div[data-category="' + indexResults.category + '"]');
                var ref = {
                    x: (category.pos.left + (set.width() / 2)),
                    y: (containerHeight -
                        (category.pos.bottom + (set.height() / 2)))
                };
                var radius = 40;
                var perRow = 17; //Math.min(17, indexResults.count);
                var row = 1;
                var itemsInRowCount = 0;
                var angle;
                
                category.itemCount = 0;
                category.elem.css(category.pos);
                
                $.each(indexResults.members, function (index, indexResult) {
                    item = getOrAddItem(indexResult, index, category);
                    angle = itemsInRowCount * 2 * Math.PI / perRow;
                    radius = 40 + (15 * row);
                    itemsInRowCount += 1;
                    if (itemsInRowCount >= perRow) {
                        row += 1;
                        itemsInRowCount = 0;
                        perRow += (row * 2);
                    }
                    item.categoryPos = {
                        left: (ref.x - Math.cos(angle) * radius),
                        bottom: (containerHeight -
                            ref.y + Math.sin(angle) * radius)
                    };
                    item.elem.css(item.categoryPos);
                    category.itemCount += 1;
                    activeCount += 1;
                    
                    useItem(indexResult);
                });
                
                $('.hp-count', category.elem).text(category.itemCount);
                
                if (activeCount > 10) {
                    $(CONTAINER).addClass('dense');
                }
            }
            
            function onCategoryIndexResultsReset() {
                clearConnections();
                delayConnections = false;
                selectedSequence = 0;
                sequence += 1;
                viewSequence = sequence;
                filteredSequence = sequence;
                
                $.each(categories, function (name, category) {
                    category.filteredItemCount = 0;
                });
            }
          
            function onCategoriesChange(categoryResults) {
                var category;
                var factor = Math.ceil(Math.sqrt(categoryResults.length));
                var xOffset = ($(CONTAINER).width() - 40) / factor;
                var yOffset = ($(CONTAINER).height() - 0) / factor;
                var containerHeight = $(CONTAINER).height();
                var labels = [];
                
                $(SET).remove();
                $('#hp-visualizer-categories .hp-options').empty();
                
                $.each(categoryResults, function (index, categoryResult) {
                    category = getOrAddCategory(categoryResult, index);
                    var row = Math.floor(index / factor);
                    var column = Math.floor(index % factor);
                    // stagger x for a honeycomb effect
                    category.pos = {
                        bottom: (containerHeight - 40 -
                            ((yOffset / 2) + (yOffset * row))),
                        left: ((xOffset / 2) + (xOffset * column) +
                            ((row % 2) * (xOffset / 2)) - (xOffset / 4))
                    };
                    category.elem.css(category.pos);
                    
                    labels.push(linkTargetBuilder.categoryLabel(categoryResult));
                });
                
                $("#hp-visualizer-categories").hpSelect({
                    multiSelect: true,
                    options: labels,
                    selection: labels
                }).change(onFilterCategories);
                $("#hp-visualizer-categories .hp-value").
                    text('All categories');
            }
            
            /**
             * @public
             */
            this.pause = function () {
                presenter.pause();
                $(CONTAINER).unbind('click', onClickContainer);
            };
            
            this.resume = function () {
                document.title = 'Visualizer';
                presenter.resume();
                $(CONTAINER).bind('click', onClickContainer);
            };
            
            this.init = function () {
                var canvas = $(CANVAS)[0];
                if (canvas.getContext) {
                    context = canvas.getContext('2d');
                }
                $('#hp-visualizer-sort').hpSelect();
                $('#hp-visualizer-status').
                    hpSelect({selection: 'all'}).change(onFilterStatus);
                $('#hp-visualizer-sort .hp-value').text('By ' +
                    $('#hp-visualizer-sort .hp-value').text());
                
                presenter.init();
                presenter.on('categoriesChange', onCategoriesChange);
                presenter.on('categoryIndexResultsReset',
                    onCategoryIndexResultsReset);
                presenter.on('categoryIndexResultsChange',
                    onCategoryIndexResultsChange);
                presenter.on('filterIndexResultsChange',
                    onFilterIndexResultsChange);
                presenter.on('treeChange', onTreeChange);
                this.resume();
            };
        }

        return new VisualizerView();
    }());
    
    return VisualizerView;
});
