#!/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

from winswitch.util.simple_logger import Logger
from winswitch.net.twisted_bonjour import ServiceDescriptor, get_interface_index
from winswitch.net.net_util import if_indextoname
from winswitch.util.common import csv_list
from twisted.internet.defer import Deferred, AlreadyCalledError
from winswitch.net import pybonjour

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

class TwistedBonjourPublishers:
	"""
	Aggregates a number of TwistedBonjourPublisher(s).
	This takes care of constructing the appropriate TwistedBonjourPublisher with the interface index and port for the given list of (host,port)s to broadcast on.
	"""

	def __init__(self, reactor, 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), reactor, 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 = pybonjour.TXTRecord(td)
			self.publishers.append(TwistedBonjourPublisher(reactor, iface_index, port, service_name, service_type, txt))

	def start(self):
		for publisher in self.publishers:
			publisher.start()

	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 TwistedBonjourPublisher:

	def __init__(self, reactor, iface_index, port, service_name, service_type, text_record):
		Logger(self, log_colour=Logger.YELLOW)
		self.sdref = None
		self.reader = None
		self.reactor = reactor
		self.iface_index = iface_index
		self.port = port
		self.service_name = service_name
		self.service_type = service_type
		self.text_record = text_record

	def __str__(self):
		return	"TwistedBonjourPublisher(%s,%s)" % (self.iface_index, self.port)

	def broadcasting(self, args):
		self.slog(None, args)
		self.sdref  = args[0]

	def failed(self, errorCode):
		self.serror(None, errorCode)

	def	start(self):
		self.slog()
		#d = broadcast(reactor, "_daap._tcp", 3689, "DAAP Server")
		d = self.broadcast()
		if d:
			d.addCallback(self.broadcasting)
			d.addErrback(self.failed)
		return d

	def broadcast(self):
		def _callback(sdref, flags, errorCode, name, regtype, domain):
			self.sdebug(None, sdref, flags, errorCode, name, regtype, domain)
			if errorCode == pybonjour.kDNSServiceErr_NoError:
				d.callback((sdref, name, regtype, domain))
			else:
				try:
					d.errback(errorCode)
				except AlreadyCalledError, e:
					self.serror("%s" % e, sdref, flags, errorCode, name, regtype, domain)
				return

		d = Deferred()
		self.sdebug("adding service for interface %s on %s" % (self.iface_index, self.port))
		try:
			self.sdref = pybonjour.DNSServiceRegister(name = self.service_name,
	                                regtype = self.service_type,
	                                port = self.port,
	                                txtRecord = self.text_record,
	                                callBack = _callback)
		except pybonjour.BonjourError, e:
			if e.errorCode==pybonjour.kDNSServiceErr_NameConflict:
				self.serr("another server is already claiming our service type '%s'!" % self.service_type, e)
			else:
				self.serr("failed to broadcast mdns service %s on port %s - ensure that mdns is installed and running" % (self.service_type, self.port), e)
			return None

		self.reader = self.reactor.addReader(ServiceDescriptor(self.sdref))
		return d

	def stop(self):
		self.sdebug("reader=%s, sdref=%s" % (self.reader, self.sdref))
		if self.reader:
			self.reactor.removeReader(self.reader)
		if self.sdref:
			self.sdref.close()
		else:
			self.serr("not started!")



def main():
	from winswitch.util.main_loop import with_loop
	import random, signal
	from twisted.internet import reactor
	from winswitch.consts import MDNS_TYPE
	port = int(20000*random.random())+10000
	host = "0.0.0.0"
	host_ports = [(host, port)]
	ID = "test_%s" % int(random.random()*100000)
	publisher = TwistedBonjourPublisher(reactor, host_ports, ID, MDNS_TYPE, {"somename":"somevalue"}, False)
	signal.signal(signal.SIGTERM, exit)
	with_loop(publisher.start)


if __name__ == "__main__":
	main()
