// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
/**
 * @type {Map}
 */
define(['hp/model/map/MapRow', 'hp/model/map/MapLink'],
function (MapRow, MapLink) { "use strict";

    var Map = ( function() {

        function Map() {

            var self = this;
            var rows = [];
            var rowIndex = {}; // category: MapRow
            var numColumns = 0;
            var nodeIndex = {}; // nodeId: MapNode
            var selectedNode;
            var highlightedNodes = [];
            var links = [];
            var highlightedLinks = [];
            // to avoid duplicating links
            var processedLinks = {}; // parentId+childId: true
            
            function addLink(parentNode, childNode) {
                var link, linkId = parentNode.id() + childNode.id();
                if (! processedLinks[linkId]) {
                    link = new MapLink();
                    link.init(self, parentNode, childNode);
                    links.push(link);
                    if (links.length === 100000) { throw 'too many links';} ///
                    processedLinks[linkId] = true;
                }
            }
            
            // reserved for MapLink's use
            this.removeLink = function (link) {
                var index = links.indexOf(link);
                links.splice(index, 1);
                delete processedLinks[link.parentNode().id() + link.childNode().id()];
                link.destroy();
            };
            
            function getOrCreateRow(category) {
                var row = rowIndex[category];
                if (! row) {
                    row = new MapRow();
                    row.init(self, category);
                    rows.push(row);
                    rowIndex[category] = row;
                }
                return row;
            }
            
            function processCategories(mapData) {
                var length;
                var row, node;
                $.each(mapData.categories, function(category, node) {
                    row = getOrCreateRow(category);
                    length = mapData.categories[category].length;
                    for (var i=0; i<length; i++) {
                        node = row.getOrCreateNode(mapData.categories[category][i]);
                        nodeIndex[node.id()] = node;
                    }
                });
            }
            
            function processLinks(mapData) {
                var length = mapData.links.length;
                var link, parentNode, childNode;
                for (var i=0; i<length; i++) {
                    link = mapData.links[i];
                    parentNode = nodeIndex[link.parentUri];
                    childNode = nodeIndex[link.childUri];
                    
                    if (parentNode && childNode) {
                        addLink(parentNode, childNode);
                    }
                }
            }
            
            function setRowIndexes() {
                var length, i;
                length = rows.length;
                for (i=0; i<length; i++) {
                    rows[i].setIndex(i);
                }
            }
            
            function selectNode(uri) {
                var length, i, row, selectedRow;
                var ancestorColumn = 0;
                var ancestorPositive = false;
                var ancestorCount = 0;
                var descendantColumn = 0;
                var descendantPositive = false;
                var descendantCount = 0;
                
                selectedNode = nodeIndex[uri];
                selectedNode.setSelected(true);
                selectedRow = selectedNode.row();
                selectedRow.setColumn(0);
                
                // Assign single node ancestor and descendent rows
                // alternating column offsets. First will be +1, next -1,
                // then +2, then -2, and so on.
                
                // ancestors, center to top
                for (i=(selectedRow.index()-1); i>=0; i--) {
                    row = rows[i];
                    if (row.singleNode()) {
                        row.setColumn(ancestorColumn);
                        if (ancestorPositive) {
                            ancestorColumn = -ancestorColumn;
                        } else {
                            ancestorColumn = Math.abs(ancestorColumn) + 1;
                        }
                        ancestorPositive = (! ancestorPositive);
                        ancestorCount += 1;
                    }
                }
                
                // descendents, center to bottom
                length = rows.length;
                for (i=(selectedRow.index()+1); i<length; i++) {
                    row = rows[i];
                    if (row.singleNode()) {
                        row.setColumn(descendantColumn);
                        if (ancestorPositive) {
                            descendantColumn = -descendantColumn;
                        } else {
                            descendantColumn = Math.abs(descendantColumn) + 1;
                        }
                        descendantPositive = (! descendantPositive);
                        descendantCount += 1;
                    }
                }
                
                numColumns = Math.max(1, ancestorCount, descendantCount);
            }
            
            function clearHighlight() {
                var length, i, node, link;
                
                length = highlightedNodes.length;
                for (i=0; i<length; i++) {
                    node = highlightedNodes[i];
                    node.setHighlighted(false);
                }
                highlightedNodes = [];
                
                length = highlightedLinks.length;
                for (i=0; i<length; i++) {
                    link = highlightedLinks[i];
                    link.setHighlighted(false);
                }
                highlightedLinks = [];
            }
            
            /**
             * @public
             */
            this.init = function(mapData) {
                processCategories(mapData);
                processLinks(mapData);
                setRowIndexes();
                selectNode(mapData.rootUri);
            };
            
            this.destroy = function() {
                var length, i;
                
                rowIndex = null;
                nodeIndex = null;
                selectedNode = null;
                highlightedNodes = null;
                highlightedLinks = null;
                
                length = links.length;
                for (i=0; i<length; i++) {
                    links[i].destroy();
                }
                links = null;
                
                length = rows.length;
                for (i=0; i<length; i++) {
                    rows[i].destroy();
                }
                rows = null;
            };
            
            this.rows = function () {
                return rows;
            };
            
            this.links = function () {
                return links;
            };
            
            this.numColumns = function () {
                return numColumns;
            };
            
            this.selectedNode = function () {
                return selectedNode;
            };
            
            this.hasHighlight = function () {
                return highlightedNodes.length > 0;
            };
            
            this.highlightedNodes = function () {
                return highlightedNodes;
            };
            
            this.highlightedLinks = function () {
                return highlightedLinks;
            };
            
            this.highlightNode = function (id) {
                var length, i, node, link;
                
                clearHighlight();
                
                node = nodeIndex[id];
                highlightedLinks = node.links();
                
                length = highlightedLinks.length;
                for (i=0; i<length; i++) {
                    link = highlightedLinks[i];
                    link.setHighlighted(true);
                    link.parentNode().setHighlighted(true);
                    link.childNode().setHighlighted(true);
                    highlightedNodes.push(link.parentNode());
                    highlightedNodes.push(link.childNode());
                }
            };
            
            this.removeHighlight = function () {
                clearHighlight();
            };
            
            this.hasDifferentNodes = function (otherMap) {
                var result = true;
                var length, otherRows = otherMap.rows();
                if (rows.length === otherRows.length) {
                    result = false;
                    length = rows.length;
                    for (var i=0; i<length; i++) {
                        if (rows[i].hasDifferentNodes(otherRows[i])) {
                            result = true;
                            break;
                        }
                    }
                }
                return result;
            };
        }

        return Map;
    }());

    return Map;
});
