#!{{ python_executable }}
# Put this file in the software release
promise_runner_path = "{{ promise_runner_path }}"
public_folder = "{{ public_folder }}"
private_folder = "{{ private_folder }}"
monitor_configuration_path = "{{ monitor_configuration_path }}"
promise_folder = "{{ promise_folder }}"
monitor_promise_folder = "{{ monitor_promise_folder }}"
promise_wrapper_folder = "{{ promise_wrapper_folder }}"

import sys
import os
import stat
import subprocess
import threading
import json
import ConfigParser
import traceback


def main():
  # initialisation
  config = loadConfig([monitor_configuration_path])
  # get promises in monitor_promise_folder
  promise_dict = {}
  fillPromiseDictFromFolder(promise_dict, monitor_promise_folder)
  # get promises in promise_folder
  fillPromiseDictFromFolder(promise_dict, promise_folder)
  # get promises configurations
  for filename in os.listdir(monitor_promise_folder):
    path = os.path.join(monitor_promise_folder, filename)
    if os.path.isfile(path) and filename[-4:] == ".cfg":
      promise_name = filename[:-4]
      if promise_name in promise_dict:
        loadConfig([path], promise_dict[promise_name]["configuration"])
  promise_items = promise_dict.items()
  # create symlinks from service configurations
  for service_name, promise in promise_items:
    service_config = promise["configuration"]
    createSymlinksFromConfig((config, "monitor", "public-folder"), (service_config, "service", "public-path-list"), service_name)
    createSymlinksFromConfig((config, "monitor", "private-folder"), (service_config, "service", "private-path-list"), service_name)
  # create symlinks from monitor.conf
  createSymlinksFromConfig((config, "monitor", "public-folder"), (config, "monitor", "public-path-list"))
  createSymlinksFromConfig((config, "monitor", "private-folder"), (config, "monitor", "private-path-list"))
  # generate monitor.json
  monitor_dict = {}
  tmp = softConfigGet(config, "monitor", "title")
  if tmp:
    monitor_dict["title"] = tmp
  tmp = softConfigGet(config, "monitor", "monitor-url-list")
  if tmp:
    monitor_dict["_links"] = {"related_monitor": [{"href": url} for url in tmp.split()]}
  if promise_items:
    service_list = []
    monitor_dict["_embedded"] = {"service": service_list}
    for service_name, promise in promise_items:
      service_config = promise["configuration"]
      service_dict = {}
      service_list.append(service_dict)
      service_dict["id"] = service_name
      service_dict["_links"] = {"status": {"href": "/public/%s.status.json" % service_name}}  # hardcoded
      tmp = softConfigGet(service_config, "service", "title")
      if tmp:
        service_dict["title"] = tmp
      interface_path = os.path.join(private_folder, service_name, "interface/index.html")  # hardcoded
      if os.path.isfile(interface_path):
        service_dict["_links"]["interface"] = {"href": "/private/%s/interface/" % service_name}  # hardcoded
      else:
        service_dict["_links"]["interface"] = {"href": "/default-promise-interface.html?service_name=%s" % service_name}  # XXX hardcoded
  with open(config.get("monitor", "monitor-hal-json"), "w") as fp:
    json.dump(monitor_dict, fp)
  # put promises to a cron file
  # XXX only if at least one configuration file is modified, then write in the cron
  service_pid_folder = config.get("monitor", "service-pid-folder")
  crond_folder = config.get("monitor", "crond-folder")
  cron_line_list = []
  for service_name, promise in promise_items:
    service_status_path = "%s/%s.status.json" % (public_folder, service_name)  # hardcoded
    mkdirAll(os.path.dirname(service_status_path))
    command = ("%s %s %s " % (
      promise_runner_path,
      os.path.join(service_pid_folder, "%s.pid" % service_name),
      service_status_path,
    )) + promise["path"]
    cron_line_list.append("%s %s" % (
      softConfigGet(service_config, "service", "frequency") or "* * * * *",
      command.replace("%", "\\%"),
    ))
    wrapper_path = os.path.join(promise_wrapper_folder, service_name)
    with open(wrapper_path, "w") as fp:
      fp.write("#!/bin/sh\n%s" % command)  # XXX hardcoded, use dash, sh or bash binary!
    os.chmod(wrapper_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IROTH )
  with open(crond_folder + "/monitor-promises", "w") as fp:
    fp.write("\n".join(cron_line_list))
  return 0

def loadConfig(pathes, config=None):
  if config is None:
    config = ConfigParser.ConfigParser()
  try:
    config.read(pathes)
  except ConfigParser.MissingSectionHeaderError:
    traceback.print_exc()
  return config

def fillPromiseDictFromFolder(promise_dict, folder):
  for filename in os.listdir(folder):
    path = os.path.join(folder, filename)
    if os.path.isfile(path) and os.access(path, os.X_OK):
      promise_dict[filename] = {"path": path, "configuration": ConfigParser.ConfigParser()}

def softConfigGet(config, *args, **kwargs):
  try:
    return config.get(*args, **kwargs)
  except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
    return None

def createSymlinksFromConfig(destination_folder_config_tuple, source_list_config_tuple, service_name=""):
  destination_folder = softConfigGet(*destination_folder_config_tuple)
  if destination_folder:
    source_path_str = softConfigGet(*source_list_config_tuple)
    if source_path_str:
      for path in source_path_str.split():
        dirname = os.path.join(destination_folder, service_name)
        try:
          mkdirAll(dirname)  # could also raise OSError
          os.symlink(path, os.path.join(dirname, os.path.basename(path)))
        except OSError, e:
          if e.errno != os.errno.EEXIST:
            raise

def mkdirAll(path):
  try:
    os.makedirs(path)
  except OSError, e:
    if e.errno == os.errno.EEXIST and os.path.isdir(path):
      pass
    else: raise

if __name__ == "__main__":
  sys.exit(main())