#!/usr/bin/env python
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php

# Copyright 2006,2007,2008 Frank Scholz <coherence@beebits.net>
# Copyright 2011, Hartmut Goebel <h.goebel@goebel-consult.de>

""" Coherence is a framework to host DLNA/UPnP devices

    For more information about it and its available backends
    point your browser to: http://coherence-project.org
"""
import errno
import sys
import os
import traceback
import warnings

from configobj import ConfigObj

from coherence import __version__


warnings.filterwarnings('ignore', "extern.louie will soon be deprecated")
warnings.filterwarnings('ignore', "coherence.extern.inotify is deprecated.")


def daemonize():
  # thankfully taken from twisted.scripts._twistd_unix.py
  # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
  if os.fork():   # launch child and...
    os._exit(0)   # kill off parent
  os.setsid()
  if os.fork():   # launch child and...
    os._exit(0)   # kill off parent again.
  os.umask(077)
  null = os.open('/dev/null', os.O_RDWR)
  for i in range(3):
    try:
      os.dup2(null, i)
    except OSError, e:
      if e.errno != errno.EBADF:
        raise
  os.close(null)


def get_config_file():
  config_dir = os.path.expanduser('~')
  if config_dir == '~':
    config_dir = os.getcwd()

  return os.path.join(config_dir, '.cohen')


def __opt_option(option, opt, value, parser):
  try:
    key, val = value.split(':', 1)
  except:
    key = value
    val = ''
  parser.values.options[key] = val


def main(config):
  from coherence.base import Coherence
  Coherence(config)

if __name__ == '__main__':
  import optparse

  class OptionParser(optparse.OptionParser):
      """
      Simple wrapper to add list of available plugins to help
      message, but only if help message is really printed
      """

      def print_help(self, file=None):
          from coherence.base import Plugins
          # hack: avoid plugins are displaying there help message
          sys.argv = sys.argv[:1]
          p = Plugins().keys()
          p.sort()
          self.epilog = 'Available backends are: %s' % ', '.join(p)
          optparse.OptionParser.print_help(self, file)

  parser = OptionParser('%prog [options]', version="Coherence version: %s" % __version__)
  parser.add_option('-d', '--daemon', action='store_true',
                    help='daemonize')
  parser.add_option('--noconfig', action='store_false', dest='configfile',
                    help='ignore any configfile found')
  parser.add_option('-c', '--configfile', default=get_config_file(),
                    help='configfile to use, default: %default')
  parser.add_option('-l', '--logfile', help='logfile to use')
  parser.add_option('-o', '--option', action='callback',
                    dest='options', metavar='NAME:VALUE',
                    default={}, callback=__opt_option, type='string',
                    help="activate option (name and value separated by a "
                         "colon (`:`), may be given multiple times)")
  parser.add_option('-p', '--plugins', action='append',
                    help='activate plugins (may be given multiple times) '
                         'Example: --plugin=backend:FSStore,name:MyCoherence')

  options, args = parser.parse_args()
  if args:
    parser.error('takes no arguments')

  if options.daemon:
    try:
      daemonize()
    except:
      print traceback.format_exc()

  config = {}

  if options.configfile:
    try:
      config = ConfigObj(options.configfile)
    except IOError:
      print "Config file %r not found, ignoring" % options.configfile
      pass

  if 'logging' not in config:
    config['logging'] = {}

  if options.logfile:
    config['logging']['logfile'] = options.logfile

  # copy options passed by -o/--option into config
  for k, v in options.options.items():
    if k == 'logfile':
      continue
    config[k] = v

  if options.daemon:
    if config['logging'].get('logfile') is None:
      config['logging']['level'] = 'none'
      config['logging'].pop('logfile', None)

  if config.get('use_dbus', 'no') == 'yes' or config.get('glib', 'no') == 'yes' \
     or config.get('transcoding', 'no') == 'yes':
    try:
      from twisted.internet import glib2reactor
      glib2reactor.install()
    except AssertionError:
      print "error installing glib2reactor"

  if options.plugins:
    plugins = config.get('plugin')
    if isinstance(plugins, dict):
        config['plugin'] = [plugins]
    if not plugins:
      plugins = config.get('plugins', None)
    if not plugins:
      config['plugin'] = []
      plugins = config['plugin']

    while len(options.plugins) > 0:
      p = options.plugins.pop()
      plugin = {}
      plugin_conf = p.split(',')
      for pair in plugin_conf:
        pair = pair.split(':', 1)
        if len(pair) == 2:
          pair[0] = pair[0].strip()
          if pair[0] in plugin:
            if not isinstance(plugin[pair[0]], list):
              new_list = [plugin[pair[0]]]
              plugin[pair[0]] = new_list
            plugin[pair[0]].append(pair[1])
          else:
            plugin[pair[0]] = pair[1]
      try:
        plugins.append(plugin)
      except AttributeError:
        print "mixing commandline plugins and configfile does not work with the old config file format"

  from twisted.internet import reactor

  reactor.callWhenRunning(main, config)
  reactor.run()
