Added in support for navigating to Topo View from Intent View

Change-Id: Ia62428dee29013cc7fa52727662b67f5673d725c
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/forcesvg.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/forcesvg.component.ts
index adc814e..eebe851 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/forcesvg.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/forcesvg.component.ts
@@ -532,6 +532,7 @@
     /**
      * When traffic monitoring is turned on (A key) highlights will be sent back
      * from the WebSocket through the Traffic Service
+     * Also handles Intent highlights in case one is selected
      * @param devices - an array of device highlights
      * @param hosts - an array of host highlights
      * @param links - an array of link highlights
@@ -566,21 +567,30 @@
         }
         if (links.length > 0) {
             this.log.debug(links.length, 'Links highlighted');
-            links.forEach((lh) => {
-                const linkComponent: LinkSvgComponent =
-                    this.links.find((l) => l.link.id === Link.linkIdFromShowHighlights(lh.id) );
-                if (linkComponent) { // A link might not be present if hosts viewing is switched off
-                    if (fadeMs > 0) {
-                        lh.fadems = fadeMs;
-                    }
-                    linkComponent.ngOnChanges(
-                        {'linkHighlight': new SimpleChange(<LinkHighlight>{}, lh, true)}
-                    );
+            links.forEach((lh: LinkHighlight) => {
+                if (fadeMs > 0) {
+                    lh.fadems = fadeMs;
                 }
+                // Don't user .filter() above as it will create a copy of the component which will be discarded
+                this.links.forEach((l) => {
+                    if (l.link.id === Link.linkIdFromShowHighlights(lh.id)) {
+                        l.linkHighlight = lh;
+                        l.ngOnChanges(
+                            <SimpleChanges>{'linkHighlight': new SimpleChange(<LinkHighlight>{}, lh, true)}
+                        );
+                    }
+                });
             });
         }
     }
 
+    cancelAllLinkHighlightsNow() {
+        this.links.forEach((link: LinkSvgComponent) => {
+            link.linkHighlight = <LinkHighlight>{};
+            this.ngOnChanges(<SimpleChanges>{'linkHighlight': new SimpleChange(<LinkHighlight>{}, <LinkHighlight>{}, false)});
+        });
+    }
+
     /**
      * As nodes are dragged around the graph, their new location should be sent
      * back to server
@@ -665,6 +675,5 @@
         this.graph.reinitSimulation();
         return numbernodes;
     }
-
 }
 
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css
index 58548c4..e5f12ae 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css
@@ -86,7 +86,7 @@
 }
 
 .link.animated {
-    stroke-dasharray: 8 5;
+    stroke-dasharray: 8;
     animation: ants 5s infinite linear;
     /* below line could be added via Javascript, based on path, if we cared
      * enough about the direction of ant-flow
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html
index a94914d..ec3afae 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html
@@ -37,11 +37,12 @@
 <svg:line xmlns:svg="http://www.w3.org/2000/svg"
         [attr.x1]="link.source?.x" [attr.y1]="link.source?.y"
         [attr.x2]="link.target?.x" [attr.y2]="link.target?.y"
-        [ngClass]="['link', selected?'selected':'', enhanced?'enhanced':'', highlighted]"
+        [ngClass]="['link', selected?'selected':'', enhanced?'enhanced':'', highlightAsString()]"
         [ngStyle]="{'stroke-width': (enhanced ? 4 : 2) * scale + 'px'}"
         (click)="toggleSelected(link, $event)"
-        (mouseover)="enhance()"
-        [attr.filter]="highlighted?'url(#glow)':'none'">
+        (mouseover)="enhance()">
+<!--        [attr.filter]="highlighted?'url(#glow)':'none'">-->
+    <svg:desc>{{link.id}} {{linkHighlight?.css}} {{isHighlighted}}</svg:desc>
 </svg:line>
 <svg:g xmlns:svg="http://www.w3.org/2000/svg"
        [ngClass]="['linkLabel']"
@@ -58,7 +59,7 @@
               [@linkLabelVisible]="isHighlighted"
               [attr.x]="link.source?.x + (link.target?.x - link.source?.x)/2"
               [attr.y]="link.source?.y + (link.target?.y - link.source?.y)/2"
-    >{{ label }}</svg:text>
+    >{{ linkHighlight?.label }}</svg:text>
 </svg:g>
 <!-- Template explanation: Creates an SVG Group if
     line 1) 'enhanced' is active and port text exists
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts
index c669c1c..197ac3c 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts
@@ -28,6 +28,10 @@
     y: number;
 }
 
+/*
+ * LinkSvgComponent gets its data from 2 sources - the force SVG regionData (which
+ * gives the Link below), and other state data here.
+ */
 @Component({
     selector: '[onos-linksvg]',
     templateUrl: './linksvg.component.html',
@@ -47,9 +51,8 @@
 })
 export class LinkSvgComponent extends NodeVisual implements OnChanges {
     @Input() link: Link;
-    @Input() highlighted: string = '';
+    @Input() linkHighlight: LinkHighlight;
     @Input() highlightsEnabled: boolean = true;
-    @Input() label: string;
     @Input() scale = 1.0;
     isHighlighted: boolean = false;
     @Output() selectedEvent = new EventEmitter<SelectedEvent>();
@@ -70,23 +73,28 @@
         if (changes['linkHighlight']) {
             const hl: LinkHighlight = changes['linkHighlight'].currentValue;
             clearTimeout(this.lastTimer);
-            this.highlighted = hl.css;
-            this.label = hl.label;
             this.isHighlighted = true;
-            this.log.debug('Link hightlighted', this.link.id, this.highlighted);
+            this.log.debug('Link highlighted', this.link.id);
+
             if (hl.fadems > 0) {
                 this.lastTimer = setTimeout(() => {
                     this.isHighlighted = false;
-                    this.highlighted = '';
+                    this.linkHighlight = <LinkHighlight>{};
                     this.ref.markForCheck();
-                }, hl.fadems); // Disappear slightly before next one comes in
+                }, this.linkHighlight.fadems); // Disappear slightly before next one comes in
             }
-
         }
 
         this.ref.markForCheck();
     }
 
+    highlightAsString(): string {
+        if (this.linkHighlight && this.linkHighlight.css) {
+            return this.linkHighlight.css;
+        }
+        return '';
+    }
+
     enhance() {
         if (!this.highlightsEnabled) {
             return;
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology.service.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology.service.ts
index f2a1f60..fab3db2 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology.service.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology.service.ts
@@ -27,6 +27,21 @@
 } from './layer/forcesvg/models';
 
 /**
+ * Model of the Intent to be displayed
+ */
+export interface Intent {
+    appId: string;
+    appName: string;
+    key: string;
+    type: string;
+}
+
+export interface RelatedIntent {
+    ids: string[];
+    hover: string;
+}
+
+/**
  * ONOS GUI -- Topology Service Module.
  */
 @Injectable()
@@ -87,7 +102,8 @@
                 }
             ],
             ['showHighlights', (event) => {
-                force.handleHighlights(event.devices, event.hosts, event.links);
+                this.log.debug('Handling showHighlights', event);
+                force.handleHighlights(event.devices, event.hosts, event.links, 5000);
             }]
             // topo2Highlights is handled by TrafficService
         ]));
@@ -96,6 +112,7 @@
         this.handlers.push('topo2CurrentRegion');
         this.handlers.push('topo2PeerRegions');
         this.handlers.push('topo2UiModelEvent');
+        this.handlers.push('showHighlights');
         // this.handlers.push('topo2Highlights');
 
         // in case we fail over to a new server,
@@ -124,4 +141,30 @@
         // tell the server we are ready to receive topo events
         this.wss.sendEvent('topo2Start', {});
     }
+
+    /*
+     * Result will be handled by showHighlights handler (set up in topology service)
+     * which will call handleHighlights() in Force Component
+     */
+    setSelectedIntent(selectedIntent: Intent): void {
+        this.log.debug('Selected intent changed to', selectedIntent);
+        this.wss.sendEvent('selectIntent', selectedIntent);
+    }
+
+    selectRelatedIntent(ids: string[]): void {
+        this.log.debug('Select next intent');
+        this.wss.sendEvent('requestNextRelatedIntent', <RelatedIntent>{
+            ids: ids,
+            hover: undefined,
+        });
+    }
+
+    /*
+     * Tell the backend to stop sending highlights - any present will fade after 5 seconds
+     * There is also a cancel traffic for Topo 2 in Traffic Service
+     */
+    cancelHighlights(): void {
+        this.wss.sendEvent('cancelTraffic', {});
+        this.log.debug('Highlights canceled');
+    }
 }
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
index 29dab54..ba99fc8 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
@@ -27,7 +27,7 @@
 import { SummaryComponent } from '../panel/summary/summary.component';
 import { ToolbarComponent } from '../panel/toolbar/toolbar.component';
 import { DetailsComponent } from '../panel/details/details.component';
-import { TopologyService } from '../topology.service';
+import {Intent, TopologyService} from '../topology.service';
 
 import {
     FlashComponent,
@@ -86,6 +86,9 @@
         ];
     }
     destroy() {}
+    setSelectedIntent(selectedIntent: Intent): void {}
+    selectRelatedIntent(ids: string[]): void {}
+    cancelHighlights(): void {}
 }
 
 class MockIconService {
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
index 0aeaee7..fb4e4cb 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 import {
+    AfterViewInit,
     Component, HostListener, Inject, Input,
     OnDestroy,
     OnInit, SimpleChange,
@@ -37,7 +38,7 @@
 import {DetailsComponent} from '../panel/details/details.component';
 import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
 import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
-import {TopologyService} from '../topology.service';
+import {Intent, TopologyService} from '../topology.service';
 import {
     GridDisplayToggle,
     HostLabelToggle,
@@ -69,6 +70,7 @@
 import {MapObject} from '../layer/maputils';
 import {LayoutService, LayoutType} from '../layout.service';
 import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual';
+import {ActivatedRoute} from '@angular/router';
 
 const TOPO2_PREFS = 'topo2_prefs';
 const TOPO_MAPID_PREFS = 'topo_mapid';
@@ -143,7 +145,7 @@
   templateUrl: './topology.component.html',
   styleUrls: ['./topology.component.css']
 })
-export class TopologyComponent implements OnInit, OnDestroy {
+export class TopologyComponent implements OnInit, OnDestroy, AfterViewInit {
     @Input() bannerHeight: number = 48;
     // These are references to the components inserted in the template
     @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent;
@@ -194,6 +196,7 @@
         protected is: IconService,
         private lion: LionService,
         private layout: LayoutService,
+        protected ar: ActivatedRoute,
         @Inject('Window') public window: any,
     ) {
         if (this.lion.ubercache.length === 0) {
@@ -243,7 +246,6 @@
         this.is.loadIconDef('roadm_otn');
         this.is.loadIconDef('triangleUp');
         this.is.loadIconDef('uiAttached');
-        this.log.debug('Topology component constructed');
     }
 
     /**
@@ -298,14 +300,6 @@
         // Service just to compartmentalize things a bit
         this.ts.init(this.instance, this.background, this.force);
 
-        // Scale the window initially - then after resize
-        const zoomMapExtents = ZoomUtils.zoomToWindowSize(
-            this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
-        this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
-        this.log.debug('TopologyComponent initialized',
-            this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
-            zoomMapExtents);
-
         // For the 2.1 release to not listen to updates of prefs as they are
         // only the echo of what we have sent down and the event mechanism
         // does not discern between users. Can get confused if multiple windows open
@@ -314,6 +308,37 @@
         this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
         this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
         this.trs.init(this.force);
+
+        // Scale the window initially - then after resize
+        const zoomMapExtents = ZoomUtils.zoomToWindowSize(
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
+        this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
+
+        // TODO find out why the following is never printed
+        this.log.debug('TopologyComponent initialized,',
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
+            zoomMapExtents);
+    }
+
+    ngAfterViewInit(): void {
+        this.ar.queryParams.subscribe(params => {
+            const intentId = params['intentId'];
+            const intentType = params['intentType'];
+            const appId = params['appId'];
+            const appName = params['appName'];
+
+            if (intentId && intentType && appId) {
+                const selectedIntent = <Intent>{
+                    key: intentId,
+                    type: intentType,
+                    appId: appId,
+                    appName: appName,
+                };
+                this.ts.setSelectedIntent(selectedIntent);
+
+                this.log.warn('TopologyComponent init with Intent: ', selectedIntent, params);
+            }
+        });
     }
 
     /**
@@ -744,6 +769,8 @@
                 }
             );
         }
+        this.ts.cancelHighlights();
+        this.force.cancelAllLinkHighlightsNow();
     }
 
     /**