# 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 copy
import json
import os
import os.path

import blinker
import ipywidgets as widgets

from .config import CFG

REPOTYPES = ["Directory", "Git", "Mercurial"]

def output (data):
  """Quick whitespace stripper"""
  if data.strip():
    print(data)

def call (cmd, user = None, timeout = None):
  if user:
    if user == "root" and os.getuid() != 0:
      cmd = "sudo -H %s" % (cmd)
    else:
      cmd = "sudo -u %s %s" % (user, cmd)

  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  try:
    (sout, serr) = p.communicate(timeout = timeout)
  except TimeoutExpired:
    p.kill()
    (sout, serr) = p.communicate()
  return (sout, serr, p.returncode)

class RepositoryViewer():
  def __init__ (self, mgr):
    self._mgr = mgr

    self._name = None
    self._info = None

    self._w_name = widgets.Text(description = "Name:")

    self._w_rtype = widgets.Dropdown(options = REPOTYPES, description = "Type:")
    self._w_rtype.observe(self._rtype_observe, names=["value"])

    self._w_dlocation = widgets.Text(description = "Path:")
    self._w_rurl = widgets.Text(description = "URL:")
    self._w_rbranch = widgets.Text(description = "Branch:")

    self._w_button_save = widgets.Button(description="Save")
    self._w_button_save.on_click(self.save)

    self._w_vbox = widgets.VBox([], layout = widgets.Layout(width="69%"))

  def _update_info (self):
    self._info["name"] = self._w_name.value
    self._info["type"] = self._w_rtype.value
    self._info["location"] = self._w_dlocation.value
    self._info["url"] = self._w_rurl.value
    self._info["branch"] = self._w_rbranch.value

  def _rtype_observe (self, change):
    self._update_info()
    self._populate()

  def _populate (self):
    if not self._info:
      self._info["name"] = self._name
      self._info["type"] = "Directory"
      self._info["location"] = ""
      self._info["url"] = ""
      self._info["branch"] = ""
      self._info["last-update"] = None

    self._w_name.value = self._info["name"]
    self._w_rtype.value = self._info["type"]
    self._w_dlocation.value = self._info["location"]
    self._w_rurl.value = self._info["url"]
    self._w_rbranch.value = self._info["branch"]

    wlist = [self._w_name, self._w_rtype]
    if self._info["type"] == "Directory":
      wlist.append(self._w_dlocation)
    elif self._info["type"] in ["Git", "Mercurial"]:
      wlist.extend([self._w_rurl, self._w_rbranch])

    wlist.append(self._w_button_save)
      
    self._w_vbox.children = wlist

  def clear (self):
    self._w_vbox.children = []

  def update (self):
    if not self._mgr._w_repo_select.value:
      self.clear()
      return

    self._name = self._mgr._w_repo_select.value
    self._info = copy.copy(self._mgr._repoinfo[self._name])

    self._populate()

  def save (self, button = None):
    # This can end up putting spurious data in the unused fields for the type, but meh.
    self._name = self._w_name.value
    self._update_info()

    self._mgr._repoinfo[self._name] = copy.copy(self._info)
    if self._name != self._mgr._w_repo_select.value:
      del self._mgr._repoinfo[self._mgr._w_repo_select.value]
      self._mgr.refresh(self._name)
    self._mgr.save()

  def display (self):
    return self._w_vbox


class RepoManager():
  def __init__ (self):
    self._repoinfo = {}
    if os.path.exists("%s/model_repos.json" % (CFG.ddir)):
      self._repoinfo = json.loads(open("%s/model_repos.json" % (CFG.ddir), "r").read())

    self._rv = RepositoryViewer(self)

    self._sig_update = blinker.signal("repomgr.update")

    self._w_repo_select = widgets.Select(layout = widgets.Layout(width="auto"))
    self._w_repo_select.options = sorted([x for x in self._repoinfo.keys()])
    self._w_repo_select.observe(self._repo_select, names=["value"])

    self._w_repo_addbutton = widgets.Button(icon="plus-circle", layout = widgets.Layout(width="50px"), tooltip = "Add Repository")
    self._w_repo_addbutton.on_click(self._buttonAddRepo)
    self._w_repo_delbutton = widgets.Button(icon="minus-circle", layout = widgets.Layout(width="50px"), tooltip = "Remove Repository")
    self._w_repo_delbutton.on_click(self._buttonDelRepo)
    self._w_button_box = widgets.HBox([self._w_repo_addbutton, self._w_repo_delbutton])
    self._w_leftbox = widgets.VBox([self._w_repo_select, self._w_button_box], layout = widgets.Layout(width="30%"))

    self._w_hbox = widgets.HBox([self._w_leftbox, self._rv.display()], layout = widgets.Layout(width="99%"))

  def _buttonAddRepo (self, _):
    if "Unnamed" in self._repoinfo:
      # You have to name a repo before you can add a new one
      return

    newopts = list(self._w_repo_select.options)
    newopts.append("Unnamed")
    newopts.sort()

    self._repoinfo["Unnamed"] = {}

    self._w_repo_select.options = newopts
    self._w_repo_select.value = "Unnamed"
    self._rv.update()

  def _buttonDelRepo (self, _):
    # We probably should delete any repo contents we're managing (hg and git only)
    del self._repoinfo[self._w_repo_select.value]
    self.save()
    self._w_repo_select.index = None
    self._w_repo_select.options = sorted([x for x in self._repoinfo.keys()])

  def save (self):
    with open("%s/model_repos.json" % (CFG.ddir), "w+") as mrf:
      mrf.write(json.dumps(self._repoinfo))
    self._sig_update.send()


  def refresh (self, name):
    self._w_repo_select.options = sorted([x for x in self._repoinfo.keys()])
    self._w_repo_select.value = name
    self._rv.update()

  def _repo_select (self, change):
    self._rv.update()

  def display (self):
    return self._w_hbox
