From ba4aad9dd4623b752f1d0a2dbc4d39cb0c07335c Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Mon, 4 Jan 2021 17:39:20 +0100
Subject: [PATCH] WIP nehuba

---
 src/ui/nehubaContainer/nehuba.module.ts       |  22 ---
 .../nehubaContainer.component.spec.ts         |  21 ++-
 .../nehubaContainer.component.ts              |  10 +-
 .../nehubaContainer.template.html             |  21 +--
 src/viewerModule/index.ts                     |   1 +
 src/viewerModule/module.ts                    |  14 ++
 src/viewerModule/nehuba/actions.ts            |  10 ++
 src/viewerModule/nehuba/constants.ts          |  69 +++++++++
 src/viewerModule/nehuba/index.ts              |   4 +
 src/viewerModule/nehuba/module.ts             |  39 +++++
 .../nehubaViewer.component.spec.ts            |   0
 .../nehubaViewer/nehubaViewer.component.ts    |  11 +-
 .../nehubaViewer/nehubaViewer.style.css       |   0
 .../nehubaViewer/nehubaViewer.template.html   |   0
 .../nehubaViewerGlue.component.ts             | 138 ++++++++++++++++++
 .../nehubaViewerGlue.style.css                |   0
 .../nehubaViewerGlue.template.html            |   5 +
 .../nehubaViewerInterface.directive.spec.ts   |   0
 .../nehubaViewerInterface.directive.ts        |   2 +-
 .../nehubaViewerTouch.directive.ts            |   0
 src/viewerModule/nehuba/store.ts              |  36 +++++
 .../nehuba}/util.ts                           |   0
 src/viewerModule/viewer.interface.ts          |  48 ++++++
 .../viewerCmp/viewerCmp.component.ts          |  12 ++
 .../viewerCmp/viewerCmp.style.css             |   0
 .../viewerCmp/viewerCmp.template.html         |   0
 26 files changed, 409 insertions(+), 54 deletions(-)
 delete mode 100644 src/ui/nehubaContainer/nehuba.module.ts
 create mode 100644 src/viewerModule/index.ts
 create mode 100644 src/viewerModule/module.ts
 create mode 100644 src/viewerModule/nehuba/actions.ts
 create mode 100644 src/viewerModule/nehuba/constants.ts
 create mode 100644 src/viewerModule/nehuba/index.ts
 create mode 100644 src/viewerModule/nehuba/module.ts
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewer/nehubaViewer.component.spec.ts (100%)
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewer/nehubaViewer.component.ts (99%)
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewer/nehubaViewer.style.css (100%)
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewer/nehubaViewer.template.html (100%)
 create mode 100644 src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
 create mode 100644 src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
 create mode 100644 src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts (100%)
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewerInterface/nehubaViewerInterface.directive.ts (99%)
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/nehubaViewerInterface/nehubaViewerTouch.directive.ts (100%)
 create mode 100644 src/viewerModule/nehuba/store.ts
 rename src/{ui/nehubaContainer => viewerModule/nehuba}/util.ts (100%)
 create mode 100644 src/viewerModule/viewer.interface.ts
 create mode 100644 src/viewerModule/viewerCmp/viewerCmp.component.ts
 create mode 100644 src/viewerModule/viewerCmp/viewerCmp.style.css
 create mode 100644 src/viewerModule/viewerCmp/viewerCmp.template.html

diff --git a/src/ui/nehubaContainer/nehuba.module.ts b/src/ui/nehubaContainer/nehuba.module.ts
deleted file mode 100644
index 0104d0c01..000000000
--- a/src/ui/nehubaContainer/nehuba.module.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { NgModule } from "@angular/core";
-import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive'
-import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
-import { CommonModule } from "@angular/common";
-@NgModule({
-  imports: [
-    CommonModule
-  ],
-  declarations: [
-    NehubaViewerContainerDirective,
-    NehubaViewerUnit
-  ],
-  exports: [
-    NehubaViewerContainerDirective,
-    NehubaViewerUnit
-  ],
-  entryComponents: [
-    NehubaViewerUnit
-  ]
-})
-
-export class NehubaModule{}
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
index 796c6ce28..3a839f318 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts
@@ -8,16 +8,14 @@ import { TouchSideClass } from "./touchSideClass.directive"
 import { MaximmisePanelButton } from "./maximisePanelButton/maximisePanelButton.component"
 import { LayoutModule } from 'src/layouts/layout.module'
 import { PureContantService, UtilModule } from "src/util"
-import { AtlasLayerSelector } from "../atlasLayerSelector/atlasLayerSelector.component"
+import { AtlasLayerSelector } from "../../atlasComponents/uiSelectors/atlasLayerSelector/atlasLayerSelector.component"
 import { StatusCardComponent } from './statusCard/statusCard.component'
 import { NehubaViewerTouchDirective } from './nehubaViewerInterface/nehubaViewerTouch.directive'
 import { MobileOverlay } from "./mobileOverlay/mobileOverlay.component"
-import { RegionMenuComponent } from "../parcellationRegion/regionMenu/regionMenu.component"
-import { DatabrowserModule } from "../databrowserModule"
+
+import { DatabrowserModule } from "../../atlasComponents/databrowserModule"
 import { SplashScreen } from "./splashScreen/splashScreen.component"
 import { CurrentLayout } from 'src/ui/config/currentLayout/currentLayout.component'
-import { RegionDirective } from 'src/ui/parcellationRegion/region.directive'
-import { RegionTextSearchAutocomplete } from "../viewerStateController/regionSearch/regionSearch.component"
 import { MobileControlNubStylePipe } from './pipes/mobileControlNubStyle.pipe'
 import { ReorderPanelIndexPipe } from './reorderPanelIndex.pipe'
 import { AuthModule } from 'src/auth'
@@ -29,7 +27,6 @@ import { NehubaModule } from './nehuba.module'
 import { CommonModule } from '@angular/common'
 import { IMPORT_NEHUBA_INJECT_TOKEN } from './nehubaViewer/nehubaViewer.component'
 import { viewerStateCustomLandmarkSelector, viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper'
-import { RenderViewOriginDatasetLabelPipe } from '../parcellationRegion/region.base'
 import { By } from '@angular/platform-browser'
 import { ARIA_LABELS } from 'common/constants'
 import { NoopAnimationsModule } from '@angular/platform-browser/animations'
@@ -38,8 +35,10 @@ import { hot } from 'jasmine-marbles'
 import { HttpClientTestingModule } from '@angular/common/http/testing'
 import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors'
 import { PANELS } from 'src/services/state/ngViewerState/constants'
-import { RegionalFeaturesModule } from '../regionalFeatures'
+import { RegionalFeaturesModule } from '../../atlasComponents/regionalFeatures'
 import { Landmark2DModule } from './2dLandmarks/module'
+import { ParcellationRegionModule } from 'src/atlasComponents/parcellationRegion'
+import { AtlasCmpParcellationModule } from 'src/atlasComponents/parcellation'
 
 const { 
   TOGGLE_SIDE_PANEL,
@@ -81,6 +80,8 @@ describe('> nehubaContainer.component.ts', () => {
           HttpClientModule,
           CommonModule,
           RegionalFeaturesModule,
+          ParcellationRegionModule,
+          AtlasCmpParcellationModule,
 
           /**
            * because the change done to pureconstant service, need to intercept http call to avoid crypto error message
@@ -97,16 +98,14 @@ describe('> nehubaContainer.component.ts', () => {
           StatusCardComponent,
           NehubaViewerTouchDirective,
           MobileOverlay,
-          RegionMenuComponent,
+          
           SplashScreen,
           CurrentLayout,
-          RegionDirective,
-          RegionTextSearchAutocomplete,
   
           // pipes
           MobileControlNubStylePipe,
           ReorderPanelIndexPipe,
-          RenderViewOriginDatasetLabelPipe,
+          
           RegionAccordionTooltipTextPipe,
         ],
         providers: [
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
index 31baa1076..63123a69b 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.ts
@@ -8,7 +8,6 @@ import { MatDrawer } from "@angular/material/sidenav";
 import { LoggingService } from "src/logging";
 import {
   CHANGE_NAVIGATION,
-  generateLabelIndexId,
   getMultiNgIdsRegionsLabelIndexMap,
   getNgIds,
   ILandmark,
@@ -26,6 +25,7 @@ import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
 import { compareLandmarksChanged } from "src/util/constants";
 import { PureContantService } from "src/util";
 import { ARIA_LABELS, IDS, CONST } from 'common/constants'
+import { serialiseParcellationRegion } from "common/util"
 import { ngViewerActionSetPerspOctantRemoval, PANELS, ngViewerActionToggleMax, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "src/services/state/ngViewerState.store.helper";
 import { viewerStateSelectRegionWithIdDeprecated, viewerStateAddUserLandmarks, viewreStateRemoveUserLandmarks, viewerStateCustomLandmarkSelector, viewerStateSelectedParcellationSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedRegionsSelector } from 'src/services/state/viewerState.store.helper'
 import { SwitchDirective } from "src/util/directives/switch.directive";
@@ -36,7 +36,6 @@ import {
 import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, calculateSliceZoomFactor, scanSliceViewRenderFn as scanFn, takeOnePipe } from "./util";
 import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive";
 import { ITunableProp } from "./mobileOverlay/mobileOverlay.component";
-import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component";
 import { viewerStateMouseOverCustomLandmark } from "src/services/state/viewerState/actions";
 import { ngViewerSelectorNehubaReady, ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors";
 import { REGION_OF_INTEREST } from "src/util/interfaces";
@@ -307,7 +306,6 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
   public hoveredPanelIndices$: Observable<number>
 
   public connectivityNumber: string
-  public connectivityLoadUrl: string
 
   constructor(
     private pureConstantService: PureContantService,
@@ -757,7 +755,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
 
         /* selectedregionindexset needs to be updated regardless of forceshowsegment */
         this.selectedRegionIndexSet = !prevParcellation || prevParcellation === selectedParcellation?
-          new Set(regions.map(({ngId = defaultNgId, labelIndex}) => generateLabelIndexId({ ngId, labelIndex }))) : new Set()
+          new Set(regions.map(({ngId = defaultNgId, labelIndex}) => serialiseParcellationRegion({ ngId, labelIndex }))) : new Set()
 
         if ( forceShowSegment === false || (forceShowSegment === null && hideSegmentFlag) ) {
           this.nehubaViewer.hideAllSeg()
@@ -818,7 +816,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
       this.selectedRegions$.pipe(
         filter(() => !!this.nehubaViewer),
       ).subscribe(regions => {
-        this.nehubaViewer.initRegions = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex }))
+        this.nehubaViewer.initRegions = regions.map(({ ngId, labelIndex }) => serialiseParcellationRegion({ ngId, labelIndex }))
       })
     )
 
@@ -1080,7 +1078,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy {
         const selectRegionIds = []
         this.multiNgIdsRegionsLabelIndexMap.forEach((map, ngId) => {
           Array.from(map.keys()).forEach(labelIndex => {
-            selectRegionIds.push(generateLabelIndexId({ ngId, labelIndex }))
+            selectRegionIds.push(serialiseParcellationRegion({ ngId, labelIndex }))
           })
         })
         this.store.dispatch(viewerStateSelectRegionWithIdDeprecated({
diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html
index b08904a09..921151abb 100644
--- a/src/ui/nehubaContainer/nehubaContainer.template.html
+++ b/src/ui/nehubaContainer/nehubaContainer.template.html
@@ -393,7 +393,6 @@
       <!--  regional features-->
       <ng-template #regionalFeaturesTmpl>
         <data-browser
-          [parcellation]="selectedParcellation"
           [disableVirtualScroll]="true"
           [regions]="regions">
         </data-browser>
@@ -572,7 +571,6 @@
             [parcellationId]="selectedParcellation['@id']"
             (setOpenState)="expansionPanel.expanded = $event"
             (connectivityNumberReceived)="connectivityNumber = $event"
-            (connectivityLoadUrl)="connectivityLoadUrl = $event"
             [accordionExpanded]="expansionPanel.expanded">
           </connectivity-browser>
         </ng-container>
@@ -592,13 +590,16 @@
     <div class="w-0 h-0"
       iav-counter
       #connectedCounterDir="iavCounter">
-
-      <hbp-connectivity-matrix-row *ngIf="region && region.name"
-        [region]="region.name + (region.status? ' - ' + region.status : '')"
-        (connectivityDataReceived)="connectedCounterDir.value = $event.detail.length"
-        class="invisible d-block h-0 w-0"
-        [loadurl]="connectivityLoadUrl">
-      </hbp-connectivity-matrix-row>
+      <!-- TODO figure out why conn browser does not work here -->
+      <!-- @fsdavid, can you take a look why this component is not emitting connectivityNumberReceived event? -->
+      <connectivity-browser *ngIf="region && region.name"
+        class="d-block h-0 w-0 overflow-hidden"
+        [region]="region"
+        [parcellationId]="selectedParcellation['@id']"
+        [accordionExpanded]="true"
+        (connectivityNumberReceived)="connectedCounterDir.value = $event">
+        
+      </connectivity-browser>
     </div>
   </mat-accordion>
 </ng-template>
@@ -667,7 +668,7 @@
     <landmark-2d-flat-cmp *ngFor="let spatialData of (selectedPtLandmarks$ | async)"
       (mouseenter)="handleMouseEnterLandmark(spatialData)"
       (mouseleave)="handleMouseLeaveLandmark(spatialData)"
-      [highlight]="spatialData.highlight ? spatialData.highlight : false"
+      [color]="spatialData.highlight ? [255, 0, 0] : [255, 255, 255]"
       [positionX]="getPositionX(panelIndex, spatialData)"
       [positionY]="getPositionY(panelIndex, spatialData)"
       [positionZ]="getPositionZ(panelIndex, spatialData)">
diff --git a/src/viewerModule/index.ts b/src/viewerModule/index.ts
new file mode 100644
index 000000000..6e5c74fbe
--- /dev/null
+++ b/src/viewerModule/index.ts
@@ -0,0 +1 @@
+export { ViewerModule } from "./module"
\ No newline at end of file
diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts
new file mode 100644
index 000000000..95ea23da8
--- /dev/null
+++ b/src/viewerModule/module.ts
@@ -0,0 +1,14 @@
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { NehubaModule } from "./nehuba";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    NehubaModule,
+  ],
+  declarations: [],
+  exports: []
+})
+
+export class ViewerModule{}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/actions.ts b/src/viewerModule/nehuba/actions.ts
new file mode 100644
index 000000000..979a485f1
--- /dev/null
+++ b/src/viewerModule/nehuba/actions.ts
@@ -0,0 +1,10 @@
+import { createAction, props } from "@ngrx/store";
+import { INgLayerInterface } from "src/services/state/ngViewerState.store";
+import { NEHUBA_VIEWER_FEATURE_KEY } from "./constants";
+
+export const actionAddNgLayer = createAction(
+  `[${NEHUBA_VIEWER_FEATURE_KEY}] [addNgLayer]`,
+  props<{
+    layers: INgLayerInterface[]
+  }>()
+)
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/constants.ts b/src/viewerModule/nehuba/constants.ts
new file mode 100644
index 000000000..31cdb1d29
--- /dev/null
+++ b/src/viewerModule/nehuba/constants.ts
@@ -0,0 +1,69 @@
+
+export const NEHUBA_VIEWER_FEATURE_KEY = 'ngViewerFeature'
+
+export interface INgLayerInterface {
+  name: string // displayName
+  source: string
+  mixability: string // base | mixable | nonmixable
+  annotation?: string //
+  id?: string // unique identifier
+  visible?: boolean
+  shader?: string
+  transform?: any
+}
+
+
+export function getNgIds(regions: any[]): string[] {
+  return regions && regions.map
+    ? regions
+      .map(r => [r.ngId, ...getNgIds(r.children)])
+      .reduce((acc, item) => acc.concat(item), [])
+      .filter(ngId => !!ngId)
+    : []
+}
+
+export function getMultiNgIdsRegionsLabelIndexMap(parcellation: any = {}, inheritAttrsOpt: any = { ngId: 'root' }): Map<string, Map<number, any>> {
+  const map: Map<string, Map<number, any>> = new Map()
+  
+  const inheritAttrs = Object.keys(inheritAttrsOpt)
+  if (inheritAttrs.indexOf('children') >=0 ) throw new Error(`children attr cannot be inherited`)
+
+  const processRegion = (region: any) => {
+    const { ngId: rNgId } = region
+    const existingMap = map.get(rNgId)
+    const labelIndex = Number(region.labelIndex)
+    if (labelIndex) {
+      if (!existingMap) {
+        const newMap = new Map()
+        newMap.set(labelIndex, region)
+        map.set(rNgId, newMap)
+      } else {
+        existingMap.set(labelIndex, region)
+      }
+    }
+
+    if (region.children && Array.isArray(region.children)) {
+      for (const r of region.children) {
+        const copiedRegion = { ...r }
+        for (const attr of inheritAttrs){
+          copiedRegion[attr] = copiedRegion[attr] || region[attr] || parcellation[attr]
+        }
+        processRegion(copiedRegion)
+      }
+    }
+  }
+
+  if (!parcellation) throw new Error(`parcellation needs to be defined`)
+  if (!parcellation.regions) throw new Error(`parcellation.regions needs to be defined`)
+  if (!Array.isArray(parcellation.regions)) throw new Error(`parcellation.regions needs to be an array`)
+
+  for (const region of parcellation.regions){
+    const copiedregion = { ...region }
+    for (const attr of inheritAttrs){
+      copiedregion[attr] = copiedregion[attr] || parcellation[attr]
+    }
+    processRegion(copiedregion)
+  }
+
+  return map
+}
diff --git a/src/viewerModule/nehuba/index.ts b/src/viewerModule/nehuba/index.ts
new file mode 100644
index 000000000..240412426
--- /dev/null
+++ b/src/viewerModule/nehuba/index.ts
@@ -0,0 +1,4 @@
+export { NehubaViewerTouchDirective } from "./nehubaViewerInterface/nehubaViewerTouch.directive"
+export { NehubaModule } from "./module"
+export { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive"
+export { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"
diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts
new file mode 100644
index 000000000..88e9c4ecd
--- /dev/null
+++ b/src/viewerModule/nehuba/module.ts
@@ -0,0 +1,39 @@
+import { NgModule } from "@angular/core";
+import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive'
+import { IMPORT_NEHUBA_INJECT_TOKEN, NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
+import { CommonModule } from "@angular/common";
+import { APPEND_SCRIPT_TOKEN } from "src/util/constants";
+import { importNehubaFactory } from "./util";
+import { NehubaViewerTouchDirective } from "./nehubaViewerInterface/nehubaViewerTouch.directive";
+import { StoreModule } from "@ngrx/store";
+import { NEHUBA_VIEWER_FEATURE_KEY } from "./constants";
+import { reducer } from "./store";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    StoreModule.forFeature(
+      NEHUBA_VIEWER_FEATURE_KEY,
+      reducer
+    )
+  ],
+  declarations: [
+    NehubaViewerContainerDirective,
+    NehubaViewerUnit,
+    NehubaViewerTouchDirective,
+  ],
+  exports: [
+    NehubaViewerContainerDirective,
+    NehubaViewerUnit,
+    NehubaViewerTouchDirective,
+  ],
+  providers: [
+    {
+      provide: IMPORT_NEHUBA_INJECT_TOKEN,
+      useFactory: importNehubaFactory,
+      deps: [ APPEND_SCRIPT_TOKEN ]
+    }
+  ]
+})
+
+export class NehubaModule{}
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.spec.ts
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
similarity index 99%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index d1edab5e0..5626c2803 100644
--- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -3,7 +3,6 @@ import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, ra
 import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip } from "rxjs/operators";
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service";
 import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store";
-import { getNgIdLabelIndexFromId } from "src/services/stateStore.service";
 
 import { LoggingService } from "src/logging";
 import { getExportNehuba, getViewer, setNehubaViewer } from "src/util/fn";
@@ -11,7 +10,7 @@ import { getExportNehuba, getViewer, setNehubaViewer } from "src/util/fn";
 import '!!file-loader?context=third_party&name=main.bundle.js!export-nehuba/dist/min/main.bundle.js'
 import '!!file-loader?context=third_party&name=chunk_worker.bundle.js!export-nehuba/dist/min/chunk_worker.bundle.js'
 import { scanSliceViewRenderFn } from "../util";
-import { intToRgb as intToColour } from 'common/util'
+import { intToRgb as intToColour, deserialiseParcRegionId } from 'common/util'
 
 const NG_LANDMARK_LAYER_NAME = 'spatial landmark layer'
 const NG_USER_LANDMARK_LAYER_NAME = 'user landmark layer'
@@ -681,9 +680,13 @@ export class NehubaViewerUnit implements OnInit, OnDestroy {
     const reduceFn: (acc: Map<string, number[]>, curr: string) => Map<string, number[]> = (acc, curr) => {
 
       const newMap = new Map(acc)
-      const { ngId, labelIndex } = getNgIdLabelIndexFromId({ labelIndexId: curr })
+      const { ngId, labelIndex } = deserialiseParcRegionId(curr)
       const exist = newMap.get(ngId)
-      if (!exist) { newMap.set(ngId, [Number(labelIndex)]) } else { newMap.set(ngId, [...exist, Number(labelIndex)]) }
+      if (!exist) {
+        newMap.set(ngId, [Number(labelIndex)])
+      } else {
+        newMap.set(ngId, [...exist, Number(labelIndex)])
+      }
       return newMap
     }
 
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.style.css b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.style.css
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.style.css
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.style.css
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.template.html
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html
rename to src/viewerModule/nehuba/nehubaViewer/nehubaViewer.template.html
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
new file mode 100644
index 000000000..84fb0edc8
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -0,0 +1,138 @@
+import { Component, Input, OnChanges, SimpleChanges, ViewChild } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { Subject } from "rxjs";
+import { ngViewerActionAddNgLayer } from "src/services/state/ngViewerState/actions";
+import { getNgIds, getMultiNgIdsRegionsLabelIndexMap } from "../constants";
+import { IViewer, TViewerEvent } from "../../viewer.interface";
+import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
+import { NehubaViewerContainerDirective } from "../nehubaViewerInterface/nehubaViewerInterface.directive";
+
+interface INgLayerInterface {
+  name: string // displayName
+  source: string
+  mixability: string // base | mixable | nonmixable
+  annotation?: string //
+  id?: string // unique identifier
+  visible?: boolean
+  shader?: string
+  transform?: any
+}
+
+
+@Component({
+  selector: 'iav-cmp-viewer-nehuba-glue',
+  templateUrl: './nehubaViewerGlue.template.html',
+  styleUrls: [
+    './nehubaViewerGlue.style.css'
+  ]
+})
+
+export class NehubaGlueCmp implements IViewer, OnChanges{
+
+  @ViewChild(NehubaViewerContainerDirective, { static: true })
+  public nehubaContainerDirective: NehubaViewerContainerDirective
+
+  public viewerEvents$ = new Subject<TViewerEvent>()
+
+  private viewerUnit: NehubaViewerUnit
+  private ngLayersRegister: {layers: INgLayerInterface[]} = {
+    layers: []
+  }
+  private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>>
+
+  @Input()
+  public selectedParcellation: any
+
+  @Input()
+  public selectedTemplate: any
+
+  ngOnChanges(sc: SimpleChanges){
+    const {
+      selectedParcellation,
+      selectedTemplate
+    } = sc
+    if (selectedTemplate.currentValue !== selectedTemplate.previousValue) {
+      this.loadTmpl(selectedTemplate.currentValue, selectedParcellation.currentValue)
+    } else if (selectedParcellation.currentValue !== selectedParcellation.previousValue) {
+
+    }
+  }
+
+  private loadParc(parcellation: any) {
+    /**
+     * parcellaiton may be undefined
+     */
+    if ( !(parcellation && parcellation.regions)) {
+      return
+    }
+
+    /**
+     * first, get all all the ngIds, including parent id from parcellation (if defined)
+     */
+    const ngIds = getNgIds(parcellation.regions).concat( parcellation.ngId ? parcellation.ngId : [])
+
+    this.multiNgIdsRegionsLabelIndexMap = getMultiNgIdsRegionsLabelIndexMap(parcellation)
+
+    this.viewerUnit.multiNgIdsLabelIndexMap = this.multiNgIdsRegionsLabelIndexMap
+    this.viewerUnit.auxilaryMeshIndices = parcellation.auxillaryMeshIndices || []
+
+    /* TODO replace with proper KG id */
+    /**
+     * need to set unique array of ngIds, or else workers will be overworked
+     */
+    this.viewerUnit.ngIds = Array.from(new Set(ngIds))
+  }
+
+  private async loadTmpl(template: any, parcellation: any) {
+    this.nehubaContainerDirective.createNehubaInstance(template)
+    this.viewerUnit = this.nehubaContainerDirective.nehubaViewerInstance
+
+    const foundParcellation = parcellation
+      && template?.parcellations?.find(p => parcellation.name === p.name)
+    this.loadParc(foundParcellation || template.parcellations[0])
+
+
+    const nehubaConfig = template.nehubaConfig
+    const initialSpec = nehubaConfig.dataset.initialNgState
+    const {layers} = initialSpec
+
+    const dispatchLayers = Object.keys(layers).map(key => {
+      const layer = {
+        name : key,
+        source : layers[key].source,
+        mixability : layers[key].type === 'image'
+          ? 'base'
+          : 'mixable',
+        visible : typeof layers[key].visible === 'undefined'
+          ? true
+          : layers[key].visible,
+        transform : typeof layers[key].transform === 'undefined'
+          ? null
+          : layers[key].transform,
+      }
+      this.ngLayersRegister.layers.push(layer)
+      return layer
+    })
+
+    this.store.dispatch(ngViewerActionAddNgLayer({
+      layer: dispatchLayers
+    }))
+  }
+
+  constructor(
+    private store: Store<any>
+  ){
+    this.viewerEvents$.next({
+      type: 'MOUSEOVER_ANNOTATION',
+      data: {}
+    })
+  }
+
+  handleViewerLoadedEvent(flag: boolean) {
+    this.viewerEvents$.next({
+      type: 'VIEWERLOADED',
+      data: flag
+    })
+  }
+
+}
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.style.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
new file mode 100644
index 000000000..2077c35c5
--- /dev/null
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
@@ -0,0 +1,5 @@
+<div
+  iav-nehuba-viewer-container
+  (iavNehubaViewerContainerViewerLoading)="handleViewerLoadedEvent($event)">
+
+</div>
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
rename to src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
similarity index 99%
rename from src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts
rename to src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
index f4a1973ae..77d0da2c0 100644
--- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
@@ -3,7 +3,7 @@ import { NehubaViewerUnit, INehubaLifecycleHook } from "../nehubaViewer/nehubaVi
 import { Store, select } from "@ngrx/store";
 import { IavRootStoreInterface } from "src/services/stateStore.service";
 import { Subscription, Observable, fromEvent } from "rxjs";
-import { distinctUntilChanged, filter, debounceTime, shareReplay, scan, map, throttleTime, switchMapTo } from "rxjs/operators";
+import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMapTo } from "rxjs/operators";
 import { StateInterface as ViewerConfigStateInterface } from "src/services/state/viewerConfig.store";
 import { getNavigationStateFromConfig, takeOnePipe } from "../util";
 import { NEHUBA_LAYER_CHANGED, CHANGE_NAVIGATION } from "src/services/state/viewerState.store";
diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
similarity index 100%
rename from src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerTouch.directive.ts
rename to src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
diff --git a/src/viewerModule/nehuba/store.ts b/src/viewerModule/nehuba/store.ts
new file mode 100644
index 000000000..f00a21703
--- /dev/null
+++ b/src/viewerModule/nehuba/store.ts
@@ -0,0 +1,36 @@
+import { createReducer } from "@ngrx/store";
+import { INgLayerInterface } from "src/services/state/ngViewerState.store";
+
+
+/**
+ * TODO port from global store to feature store
+ */
+
+enum EnumPanelMode {
+  FOUR_PANEL = 'FOUR_PANEL',
+  V_ONE_THREE = 'V_ONE_THREE',
+  H_ONE_THREE = 'H_ONE_THREE',
+  SINGLE_PANEL = 'SINGLE_PANEL',
+}
+
+interface INehubaFeature {
+  layers: INgLayerInterface[]
+  panelMode: string
+  panelOrder: string
+  octantRemoval: boolean
+  clearViewQueue: {
+    [key: string]: boolean
+  }
+}
+
+const defaultState: INehubaFeature = {
+  layers: [],
+  panelMode: EnumPanelMode.FOUR_PANEL,
+  panelOrder: '0123',
+  octantRemoval: true,
+  clearViewQueue: {}
+}
+
+export const reducer = createReducer(
+  defaultState
+)
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/util.ts b/src/viewerModule/nehuba/util.ts
similarity index 100%
rename from src/ui/nehubaContainer/util.ts
rename to src/viewerModule/nehuba/util.ts
diff --git a/src/viewerModule/viewer.interface.ts b/src/viewerModule/viewer.interface.ts
new file mode 100644
index 000000000..536a5eec6
--- /dev/null
+++ b/src/viewerModule/viewer.interface.ts
@@ -0,0 +1,48 @@
+import { Observable } from "rxjs";
+
+type TLayersColorMap = Map<string, Map<number, { red: number, green: number, blue: number }>>
+
+interface IViewerCtrl {
+
+  // navigation control
+  setNavigationLoc(coord: number[], realSpace?: boolean): void
+  moveToNavigationLoc(coord: number[], realSpace?: boolean): void
+  setNavigationOri(quat: number[]): void
+  moveToNavigationOri(quat: number[]): void
+
+  // segment control
+  showSegment(segment: any): void
+  hideSegment(segment: any): void
+  showAllSegments(): void
+  hideAllSegments(): void
+
+  // landmark control
+  addLandmarks(landmarks: any[]): void
+  removeLandmarks(landmarks: any[]): void
+
+  // layer control
+  addLayer(layerSpec: any): void
+  removeLayer(layerId: string): void
+  applyLayersColourMap(map: TLayersColorMap): void
+  getLayersColourMap(): TLayersColorMap
+}
+
+type TViewerEventMOAnno = {
+  type: "MOUSEOVER_ANNOTATION"
+  data: any
+}
+
+type TViewerEventViewerLoaded = {
+  type: "VIEWERLOADED",
+  data: boolean
+}
+
+export type TViewerEvent = TViewerEventMOAnno | TViewerEventViewerLoaded
+
+export interface IViewer{
+  
+  selectedTemplate: any
+  selectedParcellation: any
+  viewerCtrlHandler?: IViewerCtrl
+  viewerEvents$: Observable<TViewerEvent>
+}
\ No newline at end of file
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
new file mode 100644
index 000000000..ba31dac3d
--- /dev/null
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -0,0 +1,12 @@
+import { Component } from "@angular/core";
+
+@Component({
+  selector: 'iav-cmp-viewer-container',
+  templateUrl: './viewerCmp.template.html',
+  styleUrls: [
+    './viewerCmp.style.css'
+  ]
+})
+
+export class ViewerCmp{
+}
\ No newline at end of file
diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
new file mode 100644
index 000000000..e69de29bb
-- 
GitLab