import __builtin__
import os
import os.path
import sys
import glob
import ConfigParser
import ssl

CONF_DIR = os.path.expanduser("~/.burst/")
CERT_DIR = os.path.join(CONF_DIR, "certs")
SESSION_DIR = os.path.join(CONF_DIR, "sessions")
ARCHIVE_DIR = os.path.join(CONF_DIR, "archives")
PLUGIN_DIR = os.path.join(CONF_DIR, "plugins")

def get_ca_certs_path():
  certpaths = { "fedora":  "/etc/pki/tls/cert.pem",
                "ubuntu":  "/etc/ssl/certs/ca-certificates.crt",
                "suse":    "/etc/ssl/ca-bundle.pem",
                "freebsd": "/usr/local/share/certs/ca-root-nss.crt" }

  for key, path in certpaths.iteritems():
    if os.path.isfile(path):
      return path
  return None

def check_config_dir():
  existed = True
  if not os.path.exists(CONF_DIR):
    os.mkdir(CONF_DIR, 0700)
  if not os.path.exists(CERT_DIR):
    os.mkdir(CERT_DIR, 0700)
    existed = False
  if not os.path.exists(os.path.join(CERT_DIR, "sites")):
    os.mkdir(os.path.join(CERT_DIR, "sites"), 0700)
  if not os.path.exists(SESSION_DIR):
    os.mkdir(SESSION_DIR, 0700)
  if not os.path.exists(ARCHIVE_DIR):
    os.mkdir(ARCHIVE_DIR, 0700)
  if not os.path.exists(PLUGIN_DIR):
    os.mkdir(PLUGIN_DIR, 0700)
  return existed

def load_plugins():
  sys.path.append(os.path.join(CONF_DIR, "plugins"))
  for f in glob.glob(os.path.join(PLUGIN_DIR, "*.py")):
    m, ext = os.path.splitext(os.path.basename(f))
    __builtin__.__dict__.update(__import__(m, globals(), locals(), ".").__dict__)

class Configuration(object):
  """
  Class representing the configuration of Burst. You should use
  the 'conf' instance. By default contains the following attributes:

    - port: default listening port
    - ip: default listening IP address
    - proxy: outgoing proxy (support: http(s), sock4a, socks5)
    - autosave: automatically save the session when exiting
    - history: keep a copy of all the requests made
    - viewer, external_viewer: command used for v and w aliases
    - editor, diff_editor: external editors called when editing
    - play_start, play_notify, play_update: commands to execute when using Request.play
    - term_width: expected width of the terminal
    - ssl_version: ssl version used with the server (SSLv2, SSLv3, SSLv23, TLSv1, TLSv1_1, TLSv1_2) - note that different versions of the `ssl` module supports different protocols
    - ssl_verify: path to CA certs chain. If None, no verification is made
    - ssl_reverse: contact the server to determine hostname for the SSL certificate
    - ssl_hostname: static hostname to use for SSL
    - target: use to specify the target in case of proxy-unaware application
    - update_content_length: flag to automatically update the header on edition

  Each value can be modified for your local installation by creating the
  file ~/.burst/burst.conf. This file uses the ConfigParser format.
  For instance:

    [burst]
    port = 8081
    ssl_verify = None

  """
  _ssl_map = {}
  for proto in filter(lambda p: p.startswith('PROTOCOL_'), dir(ssl)):
    _ssl_map[ proto[len('_') + proto.find('_') : ] ] = ssl.__dict__[proto]

  def __init__(self):
    self.ip = '127.0.0.1'
    self.port = 8080
    self.proxy = None
    self.target = None
    self.timeout = 10
    self.delay = 0
    self.autosave = True
    self.history = True
    self.color_enabled = True
    self.term_width = "auto"
    self.viewer = '/bin/less -R {}'
    self.external_viewer = '/usr/bin/xterm -e /bin/less -R {}'
    self.editor = '/usr/bin/vim -b {}'
    self.play_start = '/usr/bin/gvim -v --servername burst -b -o2 ' \
                      '-c "set autoread" ' \
                      '-c "autocmd CursorMoved * checktime" ' \
                      '-c "autocmd CursorHold * checktime" {} {} '
    self.play_notify = '/usr/bin/gvim -v --servername burst ' \
                       '--remote-send \':silent set stl={}<CR>\''
    self.play_update = '/usr/bin/gvim -v --servername burst ' \
                       '--remote-send \':silent wincmd b<CR>\' ' \
                       '--remote-send \':silent e<CR>\' ' \
                       '--remote-send \':silent wincmd t<CR>\''
    self.diff_editor = "/usr/bin/vimdiff {} {}"
    self.ssl_hostname = None
    self.ssl_reverse = False
    self.ssl_verify = get_ca_certs_path()
    self.update_content_length = True
    # default to latest TLS version supported
    self._ssl_version = max(self._ssl_map.values())
    self._values = {"port": "getint", "proxy": "get", "timeout": "getint",
                    "ssl_version": "get", "autosave": "getboolean", "viewer": "get",
                    "external_viewer": "get", "history": "getboolean",
                    "editor": "get", "diff_editor": "get", "play_notify": "get",
                    "play_start": "get", "play_update": "get", "term_width": "get",
                    "delay": "getint", "color_enabled": "getboolean",
                    "update_content_length": "getboolean", "ip": "get",
                    "target": "get", "ssl_hostname": "get", "ssl_reverse": "getboolean",
                    "ssl_verify": "get"}

  def _get_ssl_version(self):
    for k, v in self._ssl_map.items():
      if v == self._ssl_version:
        return k

  def _set_ssl_version(self, v):
    try:
      self._ssl_version = self._ssl_map[v]
    except KeyError:
      raise Exception("Possible values are: " + ", ".join(self._ssl_map.keys()))

  ssl_version = property(_get_ssl_version, _set_ssl_version)

  def __repr__(self):
    return str(self)

  def __str__(self):
    max_l = max([len(x) for x in self._values.keys()]) + 1
    return "\n".join(sorted([(s+":").ljust(max_l) + str(getattr(self, s)) for s in self._values]))

  def import_env(self):
    if "http_proxy" in os.environ:
      conf.proxy = os.environ["http_proxy"]
      print "Using", conf.proxy, "as proxy"

  def import_dict(self, d):
    for v in self._values:
      if hasattr(d, v):
        setattr(self, v, getattr(d, v))

  def load(self):
    if os.path.exists(os.path.join(CONF_DIR, "burst.conf")):
      c = ConfigParser.RawConfigParser()
      c.read(os.path.join(CONF_DIR, "burst.conf"))
      if not c.has_section("burst"):
        raise Exception("Configuration file corrupted")
      for k, v in self._values.items():
        if c.has_option("burst", k):
          setattr(self, k, getattr(c, v).__call__("burst", k))

  def save(self, force=False):
    import burst.session
    if burst.session.user_session.name != "default" and not force:
      from burst.color import error
      print error("""The current configuration is automatically saved when
your session is saved (see save() and conf.autosave).
To make this configuration global, use conf.save(force=True)""")
      return
    c = ConfigParser.RawConfigParser()
    c.add_section('burst')
    for s in self._values:
      p = getattr(self, s)
      if not p is None:
        c.set('burst', s, p)
    with open(os.path.join(CONF_DIR, "burst.conf"), "w") as f:
      c.write(f)

conf = Configuration()
