""" Here, we need some documentation...
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import sys
import os
import types
import threading
import time
import six
from DIRAC import gLogger
from DIRAC.Core.Utilities.DIRACSingleton import DIRACSingleton


@six.add_metaclass(DIRACSingleton)
class Devloader(object):
  def __init__(self):
    self.__log = gLogger.getSubLogger("Devloader")
    self.__reloaded = False
    self.__enabled = True
    self.__reloadTask = False
    self.__stuffToClose = []
    self.__watchedFiles = []
    self.__modifyTimes = {}

  def addStuffToClose(self, stuff):
    self.__stuffToClose.append(stuff)

  @property
  def enabled(self):
    return self.__enabled

  def watchFile(self, fp):
    if os.path.isfile(fp):
      self.__watchedFiles.append(fp)
      return True
    return False

  def __restart(self):
    self.__reloaded = True

    for stuff in self.__stuffToClose:
      try:
        self.__log.always("Closing %s" % stuff)
        sys.stdout.flush()
        stuff.close()
      except BaseException:
        gLogger.exception("Could not close %s" % stuff)

    python = sys.executable
    os.execl(python, python, * sys.argv)

  def bootstrap(self):
    if not self.__enabled:
      return False
    if self.__reloadTask:
      return True

    self.__reloadTask = threading.Thread(target=self.__reloadOnUpdate)
    self.__reloadTask.setDaemon(1)
    self.__reloadTask.start()

  def __reloadOnUpdate(self):
    while True:
      time.sleep(1)
      if self.__reloaded:
        return
      for modName in sys.modules:
        modObj = sys.modules[modName]
        if not isinstance(modObj, types.ModuleType):
          continue
        path = getattr(modObj, "__file__", None)
        if not path:
          continue
        if path.endswith(".pyc") or path.endswith(".pyo"):
          path = path[:-1]
        self.__checkFile(path)
      for path in self.__watchedFiles:
        self.__checkFile(path)

  def __checkFile(self, path):
    try:
      modified = os.stat(path).st_mtime
    except Exception:
      return
    if path not in self.__modifyTimes:
      self.__modifyTimes[path] = modified
      return
    if self.__modifyTimes[path] != modified:
      self.__log.always("File system changed (%s). Restarting..." % (path))
      self.__restart()
