#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import avahi
import dbus

from winswitch.consts import MDNS_TYPE
from winswitch.util.simple_logger import Logger
from winswitch.net.net_util import get_iface, if_nametoindex, if_indextoname
from winswitch.util.common import csv_list

logger = Logger("avahi_publisher")

SHOW_INTERFACE = True			#publishes the name of the interface we broadcast from


def get_interface_index(host):
	logger.sdebug(None, host)
	if host == "0.0.0.0" or host =="" or host=="*":
		return	avahi.IF_UNSPEC

	if not if_nametoindex:
		logger.serror("cannot convert interface to index (if_nametoindex is missing), so returning 'IF_UNSPEC', avahi will publish on ALL interfaces", host)
		return	avahi.IF_UNSPEC

	iface = get_iface(host)
	if not iface:
		return	None

	return	if_nametoindex(iface)

class AvahiPublishers:
	"""
	Aggregates a number of AvahiPublisher(s).
	This takes care of constructing the appropriate AvahiPublisher
	with the interface index and port for the given list of (host,port)s to broadcast on,
	and to convert the text dict into a TXT string.
	"""

	def __init__(self, listen_on, service_name, service_type, text_dict):
		Logger(self, log_colour=Logger.YELLOW)
		self.publishers = []
		for host, port in listen_on:
			iface_index = get_interface_index(host)
			self.sdebug("iface_index(%s)=%s" % (host, iface_index), listen_on, service_name, service_type, text_dict)
			td = text_dict
			if SHOW_INTERFACE and if_indextoname:
				td = text_dict.copy()
				td["iface"] = if_indextoname(iface_index)
			txt = []
			if text_dict:
				for k,v in text_dict.items():
					txt.append("%s=%s" % (k,v))
			if host=="0.0.0.0":
				host = ""
			self.publishers.append(AvahiPublisher(service_name, port, service_type, domain="", host=host, text=txt, interface=iface_index))

	def start(self):
		for publisher in self.publishers:
			try:
				publisher.start()
			except Exception, e:
				try:
					import dbus.exceptions
					if type(e)==dbus.exceptions.DBusException:
						message = e.get_dbus_message()
						dbus_error_name = e.get_dbus_name()
						if dbus_error_name=="org.freedesktop.Avahi.CollisionError":
							self.serror("error starting publisher %s: another instance already claims this dbus name: %s, message: %s" % (publisher, e, message))
							continue
				except:
					pass
				self.serr("error on publisher %s" % publisher, e)

	def stop(self):
		self.slog("stopping: %s" % csv_list(self.publishers))
		for publisher in self.publishers:
			try:
				publisher.stop()
			except Exception, e:
				self.serr("error stopping publisher %s" % publisher, e)


class AvahiPublisher:

	def __init__(self, name, port, stype=MDNS_TYPE, domain="", host="", text="", interface=avahi.IF_UNSPEC):
		Logger(self, log_colour=Logger.YELLOW)
		self.slog(None, name, port, stype, domain, host, text, interface)
		self.name = name
		self.stype = stype
		self.domain = domain
		self.host = host
		self.port = port
		self.text = text
		self.interface = interface
		self.group = None

	def __str__(self):
		return	"AvahiPublisher(%s %s:%s interface=%s)" % (self.name, self.host, self.port, self.interface)

	def start(self):
		bus = dbus.SystemBus()
		server = dbus.Interface(bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER)

		g = dbus.Interface(bus.get_object(avahi.DBUS_NAME, server.EntryGroupNew()), avahi.DBUS_INTERFACE_ENTRY_GROUP)

		g.AddService(self.interface, avahi.PROTO_UNSPEC,dbus.UInt32(0),
					 self.name, self.stype, self.domain, self.host,
					 dbus.UInt16(self.port), self.text)

		g.Commit()
		self.group = g

	def stop(self):
		if self.group:
			self.group.Reset()


def main():
	from winswitch.util.main_loop import test_loop
	import random, signal
	port = int(20000*random.random())+10000
	host = "0.0.0.0"
	name = "test service"
	publisher = AvahiPublisher(name, port, stype=MDNS_TYPE, host=host, text="somename: somevalue")
	signal.signal(signal.SIGTERM, exit)
	test_loop(publisher.start)


if __name__ == "__main__":
	main()
