# Copyright (c) 2024  Kent State University CAE-NetLab

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
import tarfile

import blinker
import ipywidgets as widgets

class ModelBrowser():
  def __init__ (self, repomgr):
    self._repomgr = repomgr

    self._w_dropdown_repo = widgets.Dropdown(description = "Repo: ", options = sorted(self._repomgr._repoinfo.keys()))
    self._w_dropdown_repo.observe(self._loadRepo, names=["value"])
    self._w_button_refresh = widgets.Button(icon = "refresh", layout = widgets.Layout(width="50px"), tooltip = "Refresh Repository")
    self._w_button_refresh.on_click(self._refreshRepo)
    self._w_label_lastupdate = widgets.Label()
    self._w_hbox_reposelect = widgets.HBox([self._w_dropdown_repo, self._w_button_refresh])
    self._w_vbox_repo = widgets.VBox([])

    self._w_select = widgets.Select(layout = widgets.Layout(width="auto"))
    self._w_accordion = widgets.Accordion(layout = widgets.Layout(width="74%"))
    self._w_box_left = widgets.VBox([self._w_vbox_repo, self._w_select], layout = widgets.Layout(width="25%"))
    self._w_hbox = widgets.HBox([self._w_box_left, self._w_accordion], layout = widgets.Layout(width="99%"))
    self._models = {}
    self._adata = {}

    self._w_select.observe(self._model_select_observe, names=["value"])

    self._loadRepo(None)
    self._hcstyle = "text-align: right;"

    self._sig_statusupdate = blinker.signal("status.update")

    self._sig_repoupdate = blinker.signal("repomgr.update")
    self._sig_repoupdate.connect(self._reloadRepoMgr)

  def _reloadRepoMgr (self, sender):
    self._w_dropdown_repo.options = sorted(self._repomgr._repoinfo.keys())
    self._loadRepo(None)
    self._sig_statusupdate.send(message = "ModelBrowser: updated for repository change")

  def _loadRepo (self, _):
    if not self._w_dropdown_repo.value:
      self._w_vbox_repo.children = [self._w_hbox_reposelect]
      return
    self._currepo = self._repomgr._repoinfo[self._w_dropdown_repo.value]
    if self._currepo["type"] == "Directory":
      self._w_vbox_repo.children = [self._w_hbox_reposelect]
      self._w_button_refresh.disabled = True
    else:
      lastupdate = self._currepo["last-update"]
      if not lastupdate:
        lastupdate = "Never"
      self._w_label_lastupdate.value = "Last Update: %s" % (lastupdate)
      self._w_button_refresh.disabled = False
      self._w_vbox_repo.children = [self._w_hbox_reposelect, self._w_label_lastupdate]
    self._loadBundles()
    self._populateSelect()
    self._setModel(None)

  def _loadBundles (self):
    self._models = {}
    if not self._currepo["location"]:
      return  # Bail if we've never refreshed this repo

    for fname in os.listdir(self._currepo["location"]):
      if fname[-5:] != ".bndl":
        continue

      with tarfile.open("%s/%s" % (self._currepo["location"], fname)) as tf:
        mdata = json.loads(tf.extractfile("model.mdata").read())
        self._models[mdata["name"]] = mdata

  def _refreshRepo (self, button):
    return

  def _populateSelect (self):
    self._w_select.options = sorted([x for x in self._models.keys()])

  def _model_select_observe (self, change):
    self._setModel(change["new"])

  def _makeDescription (self, model):
    kvs = [("Name", "name"),
           ("Author", "author"),
           ("Desc", "description")]
    slist = []
    for text,key in kvs:
      slist.append("<tr><th style=\"%s\">%s</th><td>%s</td></tr>" % (self._hcstyle, text, model[key]))
    return ("Description", widgets.HTML("<table>%s</table>" % ("\n".join(slist))))

  def _makeConnection (self, mdata, info):
    name = "%s: %d" % (mdata["proto"].upper(), mdata["port"])
    slist = []
    kvs = [("Protocol", "proto"),
           ("Port", "port"),
           ("Duration (mean)", "conn_mean_duration"),
           ("Duration (max)", "conn_max_duration"),
           ("Type", "type"),
           ("Period", "period"),
           ("Variance", "period-variance"),
           ("Restart Interval", "restart-interval")]
    for text,key in kvs:
      try:
        slist.append("<tr><th style=\"%s\">%s</th><td>%s</td>" % (self._hcstyle, text, str(mdata[key])))
      except KeyError:
        pass
      try:
        slist.append("<tr><th style=\"%s\">%s</th><td>%s</td>" % (self._hcstyle, text, str(info[key])))
      except KeyError:
        pass

    return (name, widgets.HTML("<table>%s</table>" % ("\n".join(slist))))
    

  def _setModel (self, name):
    if name:
      model = self._models[name]
      acdata = []
      acdata.append(self._makeDescription(model))
      acdata.extend([self._makeConnection(v["mdata"], model["appmap"][k]["conn_info"]) for k,v in model["conns"].items()])
      self._w_accordion.children = [x[1] for x in acdata]
      self._w_accordion.titles = [x[0] for x in acdata]
    else:
      self._w_accordion.children = []
      self._w_accordion.titles = []
      

  def display (self):
    return self._w_hbox

