ONOS-6327: WIP - added code for details panel in Hosts view.
- still need to write back-end code for providing data.

Change-Id: Id79a1c9b52d19f2899ce7c7fdc6d217870b1f8b3
diff --git a/web/gui/src/main/webapp/app/view/host/host.css b/web/gui/src/main/webapp/app/view/host/host.css
index 71e0ff0..377918a 100644
--- a/web/gui/src/main/webapp/app/view/host/host.css
+++ b/web/gui/src/main/webapp/app/view/host/host.css
@@ -24,4 +24,80 @@
 
 #ov-host div.ctrl-btns {
     width: 45px;
-}
\ No newline at end of file
+}
+
+/* More in generic panel.css */
+
+#host-details-panel.floatpanel {
+    z-index: 0;
+}
+
+
+#host-details-panel .container {
+    padding: 8px 12px;
+}
+
+#host-details-panel .close-btn {
+    position: absolute;
+    right: 12px;
+    top: 12px;
+    cursor: pointer;
+}
+
+#host-details-panel .host-icon {
+    display: inline-block;
+    padding: 0 6px 0 0;
+    vertical-align: middle;
+}
+
+#host-details-panel h2 {
+    display: inline-block;
+    margin: 8px 0;
+}
+
+
+#host-details-panel h2 input {
+    font-size: 0.90em;
+    width: 106%;
+}
+
+#host-details-panel .top-tables {
+    font-size: 12pt;
+}
+
+#host-details-panel .top div.left {
+    float: left;
+    padding: 0 18px 0 0;
+}
+#host-details-panel .top div.right {
+    display: inline-block;
+}
+
+#host-details-panel td.label {
+    font-weight: bold;
+    text-align: right;
+    padding-right: 6px;
+}
+
+#host-details-panel .actionBtns div {
+    padding: 12px 6px;
+}
+
+#host-details-panel hr {
+    width: 100%;
+    margin: 2px auto;
+}
+
+#host-details-panel .bottom table {
+    border-spacing: 0;
+}
+
+#host-details-panel .bottom th {
+    letter-spacing: 0.02em;
+}
+
+#host-details-panel .bottom th,
+#host-details-panel .bottom td {
+    padding: 6px 12px;
+    text-align: center;
+}
diff --git a/web/gui/src/main/webapp/app/view/host/host.html b/web/gui/src/main/webapp/app/view/host/host.html
index 654f263..e88efa3 100644
--- a/web/gui/src/main/webapp/app/view/host/host.html
+++ b/web/gui/src/main/webapp/app/view/host/host.html
@@ -33,6 +33,8 @@
                 </tr>
 
                 <tr ng-repeat="host in tableData track by $index"
+                    ng-click="selectCallback($event, host)"
+                    ng-class="{selected: host.id === selId}"
                     ng-repeat-complete row-id="{{host.id}}">
                     <td class="table-icon">
                         <div icon icon-id="{{host._iconid_type}}"></div>
@@ -48,4 +50,6 @@
 
     </div>
 
+    <host-details-panel></host-details-panel>
+
 </div>
diff --git a/web/gui/src/main/webapp/app/view/host/host.js b/web/gui/src/main/webapp/app/view/host/host.js
index 6e4f71b..537bc13 100644
--- a/web/gui/src/main/webapp/app/view/host/host.js
+++ b/web/gui/src/main/webapp/app/view/host/host.js
@@ -21,16 +21,298 @@
 (function () {
     'use strict';
 
+    // injected refs
+    var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks;
+
+    // internal state
+    var detailsPanel,
+        pStartY,
+        pHeight,
+        top,
+        bottom,
+        iconDiv,
+        wSize,
+        editingName = false,
+        host;
+
+    // constants
+    var topPdg = 28,
+        ctnrPdg = 24,
+        scrollSize = 17,
+
+        pName = 'host-details-panel',
+        detailsReq = 'hostDetailsRequest',
+        detailsResp = 'hostDetailsResponse',
+        nameChangeReq = 'hostNameChangeRequest',
+        nameChangeResp = 'hostNameChangeResponse';
+
+
+    function closePanel() {
+        if (detailsPanel.isVisible()) {
+            $scope.selId = null;
+            detailsPanel.hide();
+            return true;
+        }
+        return false;
+    }
+
+    function addCloseBtn(div) {
+        is.loadEmbeddedIcon(div, 'close', 20);
+        div.on('click', closePanel);
+    }
+
+    function exitEditMode(nameH2, name) {
+        nameH2.text(name);
+        nameH2.classed('editable clickable', true);
+        editingName = false;
+        ks.enableGlobalKeys(true);
+    }
+
+    function editNameSave() {
+        var nameH2 = top.select('h2'),
+            id = $scope.panelData.id,
+            val,
+            newVal;
+
+        if (editingName) {
+            val = nameH2.select('input').property('value').trim();
+            newVal = val || id;
+
+            exitEditMode(nameH2, newVal);
+            $scope.panelData.name = newVal;
+            wss.sendEvent(nameChangeReq, { id: id, name: val });
+        }
+    }
+
+    function editNameCancel() {
+        if (editingName) {
+            exitEditMode(top.select('h2'), $scope.panelData.name);
+            return true;
+        }
+        return false;
+    }
+
+    function editName() {
+        var nameH2 = top.select('h2'),
+            tf, el;
+
+        if (!editingName) {
+            nameH2.classed('editable clickable', false);
+            nameH2.text('');
+            tf = nameH2.append('input').classed('name-input', true)
+                .attr('type', 'text')
+                .attr('value', $scope.panelData.name);
+            el = tf[0][0];
+            el.focus();
+            el.select();
+            editingName = true;
+            ks.enableGlobalKeys(false);
+        }
+    }
+
+    function handleEscape() {
+        return editNameCancel() || closePanel();
+    }
+
+    function setUpPanel() {
+        var container, closeBtn, tblDiv;
+        detailsPanel.empty();
+
+        container = detailsPanel.append('div').classed('container', true);
+
+        top = container.append('div').classed('top', true);
+        closeBtn = top.append('div').classed('close-btn', true);
+        addCloseBtn(closeBtn);
+        iconDiv = top.append('div').classed('host-icon', true);
+        top.append('h2').classed('editable clickable', true).on('click', editName);
+
+        // tblDiv = top.append('div').classed('top-tables', true);
+        // tblDiv.append('div').classed('left', true).append('table');
+        // tblDiv.append('div').classed('right', true).append('table');
+
+        top.append('hr');
+
+        // bottom = container.append('div').classed('bottom', true);
+        // bottom.append('h2').classed('ports-title', true).text('Ports');
+        // bottom.append('table');
+    }
+
+    function populateTop(details) {
+        is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
+        top.select('h2').text(details.name);
+
+        // TODO: still need to add host properties (one per line)
+    }
+
+    function populateDetails(details) {
+        setUpPanel();
+        populateTop(details);
+        detailsPanel.height(pHeight);
+    }
+
+    function respDetailsCb(data) {
+        $scope.panelData = data.details;
+        host = data.host;
+        $scope.$apply();
+    }
+
+    function respNameCb(data) {
+        if (data.warn) {
+            $log.warn(data.warn, data.id);
+            top.select('h2').text(data.id);
+        }
+    }
+
+    function createDetailsPane() {
+        detailsPanel = ps.createPanel(pName, {
+            width: wSize.width,
+            margin: 0,
+            hideMargin: 0
+        });
+        detailsPanel.el().style({
+            position: 'absolute',
+            top: pStartY + 'px'
+        });
+        $scope.hidePanel = function () { detailsPanel.hide(); };
+        detailsPanel.hide();
+    }
+
+
+    // Defines the Host View controller...
     angular.module('ovHost', [])
     .controller('OvHostCtrl',
-        ['$log', '$scope', 'TableBuilderService',
+        ['$log', '$scope',
+            '$location',
+            'TableBuilderService',
+            'FnService', 'MastService', 'PanelService', 'WebSocketService',
+            'IconService', 'NavService', 'KeyService',
 
-        function ($log, $scope, tbs) {
+        function (_$log_, _$scope_, _$location_,
+                  tbs,
+                  _fs_, _mast_, _ps_, _wss_,
+                  _is_, _ns_, _ks_) {
+
+            var params,
+                handlers = {};
+
+            $log = _$log_;
+            $scope = _$scope_;
+            $loc = _$location_;
+            fs = _fs_;
+            mast = _mast_;
+            ps = _ps_;
+            wss = _wss_;
+            is = _is_;
+            ns = _ns_;
+            ks = _ks_;
+
+            params = $loc.search();
+
+            $scope.panelData = {};
+
+            // details panel handlers
+            handlers[detailsResp] = respDetailsCb;
+            handlers[nameChangeResp] = respNameCb;
+            wss.bindHandlers(handlers);
+
+            // query for if a certain host needs to be highlighted
+            if (params.hasOwnProperty('hostId')) {
+                $scope.selId = params['hostId'];
+                wss.sendEvent(detailsReq, { id: $scope.selId });
+            }
+
+            function selCb($event, row) {
+                if ($scope.selId) {
+                    wss.sendEvent(detailsReq, { id: row.id });
+                } else {
+                    $scope.hidePanel();
+                }
+                $log.debug('Got a click on:', row);
+            }
+
             tbs.buildTable({
                 scope: $scope,
-                tag: 'host'
+                tag: 'host',
+                selCb: selCb
+            });
+
+            $scope.nav = function (path) {
+                if ($scope.selId) {
+                    ns.navTo(path, { hostId: $scope.selId });
+                }
+            };
+
+            $scope.$on('$destroy', function () {
+                wss.unbindHandlers(handlers);
             });
 
             $log.log('OvHostCtrl has been created');
-        }]);
+        }])
+
+    .directive('hostDetailsPanel',
+    ['$rootScope', '$window', '$timeout', 'KeyService',
+    function ($rootScope, $window, $timeout, ks) {
+        return function (scope) {
+            var unbindWatch;
+
+            function heightCalc() {
+                pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
+                    + mast.mastHeight() + topPdg;
+                wSize = fs.windowSize(pStartY);
+                pHeight = wSize.height;
+            }
+
+            function initPanel() {
+                heightCalc();
+                createDetailsPane();
+            }
+
+            // Safari has a bug where it renders the fixed-layout table wrong
+            // if you ask for the window's size too early
+            if (scope.onos.browser === 'safari') {
+                $timeout(initPanel);
+            } else {
+                initPanel();
+            }
+            // create key bindings to handle panel
+            ks.keyBindings({
+                enter: editNameSave,
+                esc: [handleEscape, 'Close the details panel'],
+                _helpFormat: ['esc']
+            });
+            ks.gestureNotes([
+                ['click', 'Select a row to show device details'],
+                ['scroll down', 'See more devices']
+            ]);
+
+            // if the panelData changes
+            scope.$watch('panelData', function () {
+                if (!fs.isEmptyObject(scope.panelData)) {
+                    populateDetails(scope.panelData);
+                    detailsPanel.show();
+                }
+            });
+
+            // if the window size changes
+            unbindWatch = $rootScope.$watchCollection(
+                function () {
+                    return {
+                        h: $window.innerHeight,
+                        w: $window.innerWidth
+                    };
+                }, function () {
+                    if (!fs.isEmptyObject(scope.panelData)) {
+                        heightCalc();
+                        populateDetails(scope.panelData);
+                    }
+                }
+            );
+
+            scope.$on('$destroy', function () {
+                unbindWatch();
+                ks.unbindKeys();
+                ps.destroyPanel(pName);
+            });
+        };
+    }]);
 }());